--- /dev/null
+// Copyright (c) 2018-2020 Cesanta Software Limited
+// All rights reserved
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+#include <float.h>
+#include <math.h>
+
+#include "mjson.h"
+
+#if defined(_MSC_VER)
+#define alloca(x) _alloca(x)
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER < 1700
+#define va_copy(x, y) (x) = (y)
+#define isinf(x) !_finite(x)
+#define isnan(x) _isnan(x)
+#endif
+
+static double mystrtod(const char *str, const char **end);
+
+static int mjson_esc(int c, int esc) {
+ const char *p, *esc1 = "\b\f\n\r\t\\\"", *esc2 = "bfnrt\\\"";
+ for (p = esc ? esc1 : esc2; *p != '\0'; p++) {
+ if (*p == c) return esc ? esc2[p - esc1] : esc1[p - esc2];
+ }
+ return 0;
+}
+
+static int mjson_escape(int c) {
+ return mjson_esc(c, 1);
+}
+
+static int mjson_pass_string(const char *s, int len) {
+ int i;
+ for (i = 0; i < len; i++) {
+ if (s[i] == '\\' && i + 1 < len && mjson_escape(s[i + 1])) {
+ i++;
+ } else if (s[i] == '\0') {
+ return MJSON_ERROR_INVALID_INPUT;
+ } else if (s[i] == '"') {
+ return i;
+ }
+ }
+ return MJSON_ERROR_INVALID_INPUT;
+}
+
+int mjson(const char *s, int len, mjson_cb_t cb, void *ud) {
+ enum { S_VALUE, S_KEY, S_COLON, S_COMMA_OR_EOO } expecting = S_VALUE;
+ unsigned char nesting[MJSON_MAX_DEPTH];
+ int i, depth = 0;
+#define MJSONCALL(ev) \
+ if (cb != NULL && cb(ev, s, start, i - start + 1, ud)) return i + 1;
+
+// In the ascii table, the distance between `[` and `]` is 2.
+// Ditto for `{` and `}`. Hence +2 in the code below.
+#define MJSONEOO() \
+ do { \
+ if (c != nesting[depth - 1] + 2) return MJSON_ERROR_INVALID_INPUT; \
+ depth--; \
+ if (depth == 0) { \
+ MJSONCALL(tok); \
+ return i + 1; \
+ } \
+ } while (0)
+
+ for (i = 0; i < len; i++) {
+ int start = i;
+ unsigned char c = ((const unsigned char *) s)[i];
+ int tok = c;
+ if (c == ' ' || c == '\t' || c == '\n' || c == '\r') continue;
+ // printf("- %c [%.*s] %d %d\n", c, i, s, depth, expecting);
+ switch (expecting) {
+ case S_VALUE:
+ if (c == '{') {
+ if (depth >= (int) sizeof(nesting)) return MJSON_ERROR_TOO_DEEP;
+ nesting[depth++] = c;
+ expecting = S_KEY;
+ break;
+ } else if (c == '[') {
+ if (depth >= (int) sizeof(nesting)) return MJSON_ERROR_TOO_DEEP;
+ nesting[depth++] = c;
+ break;
+ } else if (c == ']' && depth > 0) { // Empty array
+ MJSONEOO();
+ } else if (c == 't' && i + 3 < len && memcmp(&s[i], "true", 4) == 0) {
+ i += 3;
+ tok = MJSON_TOK_TRUE;
+ } else if (c == 'n' && i + 3 < len && memcmp(&s[i], "null", 4) == 0) {
+ i += 3;
+ tok = MJSON_TOK_NULL;
+ } else if (c == 'f' && i + 4 < len && memcmp(&s[i], "false", 5) == 0) {
+ i += 4;
+ tok = MJSON_TOK_FALSE;
+ } else if (c == '-' || ((c >= '0' && c <= '9'))) {
+ const char *end = NULL;
+ mystrtod(&s[i], &end);
+ if (end != NULL) i += (int) (end - &s[i] - 1);
+ tok = MJSON_TOK_NUMBER;
+ } else if (c == '"') {
+ int n = mjson_pass_string(&s[i + 1], len - i - 1);
+ if (n < 0) return n;
+ i += n + 1;
+ tok = MJSON_TOK_STRING;
+ } else {
+ return MJSON_ERROR_INVALID_INPUT;
+ }
+ if (depth == 0) {
+ MJSONCALL(tok);
+ return i + 1;
+ }
+ expecting = S_COMMA_OR_EOO;
+ break;
+
+ case S_KEY:
+ if (c == '"') {
+ int n = mjson_pass_string(&s[i + 1], len - i - 1);
+ if (n < 0) return n;
+ i += n + 1;
+ tok = MJSON_TOK_KEY;
+ expecting = S_COLON;
+ } else if (c == '}') { // Empty object
+ MJSONEOO();
+ expecting = S_COMMA_OR_EOO;
+ } else {
+ return MJSON_ERROR_INVALID_INPUT;
+ }
+ break;
+
+ case S_COLON:
+ if (c == ':') {
+ expecting = S_VALUE;
+ } else {
+ return MJSON_ERROR_INVALID_INPUT;
+ }
+ break;
+
+ case S_COMMA_OR_EOO:
+ if (depth <= 0) return MJSON_ERROR_INVALID_INPUT;
+ if (c == ',') {
+ expecting = (nesting[depth - 1] == '{') ? S_KEY : S_VALUE;
+ } else if (c == ']' || c == '}') {
+ MJSONEOO();
+ } else {
+ return MJSON_ERROR_INVALID_INPUT;
+ }
+ break;
+ }
+ MJSONCALL(tok);
+ }
+ return MJSON_ERROR_INVALID_INPUT;
+}
+
+struct msjon_get_data {
+ const char *path; // Lookup json path
+ int pos; // Current path position
+ int d1; // Current depth of traversal
+ int d2; // Expected depth of traversal
+ int i1; // Index in an array
+ int i2; // Expected index in an array
+ int obj; // If the value is array/object, offset where it starts
+ const char **tokptr; // Destination
+ int *toklen; // Destination length
+ int tok; // Returned token
+};
+
+#include <stdio.h>
+
+static int plen1(const char *s) {
+ int i = 0, n = 0;
+ while (s[i] != '\0' && s[i] != '.' && s[i] != '[')
+ n++, i += s[i] == '\\' ? 2 : 1;
+ // printf("PLEN: s: [%s], [%.*s] => %d\n", s, i, s, n);
+ return n;
+}
+
+static int plen2(const char *s) {
+ int i = 0;
+ while (s[i] != '\0' && s[i] != '.' && s[i] != '[')
+ i += s[i] == '\\' ? 2 : 1;
+ // printf("PLEN: s: [%s], [%.*s] => %d\n", s, i, s, n);
+ return i;
+}
+
+static int kcmp(const char *a, const char *b, int n) {
+ int i = 0, j = 0, r = 0;
+ for (i = 0, j = 0; j < n; i++, j++) {
+ if (b[i] == '\\') i++;
+ if ((r = a[j] - b[i]) != 0) return r;
+ }
+ // printf("KCMP: a: [%.*s], b:[%.*s] ==> %d\n", n, a, i, b, r);
+ return r;
+}
+
+static int mjson_get_cb(int tok, const char *s, int off, int len, void *ud) {
+ struct msjon_get_data *d = (struct msjon_get_data *) ud;
+#if 0
+ printf("--> %2x %2d %2d %2d %2d\t %2d %2d\t'%s' '%s' '%s' '%s'\n", tok, d->d1,
+ d->d2, d->i1, d->i2, (int) off, (int) d->pos, s, d->path, s + off,
+ d->path + d->pos);
+#endif
+ if (d->tok != MJSON_TOK_INVALID) return 1; // Found
+ if (tok == '{' || tok == '[') {
+ if (d->d1 < d->d2) d->obj = -1;
+ if (d->d1 == d->d2) d->obj = off;
+ if (d->d1 == d->d2 && tok == '[' && d->path[d->pos] == '[') {
+ d->i1 = 0;
+ d->i2 = (int) mystrtod(&d->path[d->pos + 1], NULL);
+ if (d->i1 == d->i2) {
+ while (d->path[d->pos] && d->path[d->pos] != ']') d->pos++;
+ if (d->path[d->pos] == ']') d->pos++;
+ d->d2++;
+ }
+ }
+ d->d1++;
+ } else if (tok == '}' || tok == ']') {
+ if (tok == ']' && d->d1 == d->d2) d->i1 = 0;
+ d->d1--;
+ // printf("X %s %d %d %d %d %d\n", d->path + d->pos, d->d1, d->d2, d->i1,
+ // d->i2, d->obj);
+ if (!d->path[d->pos] && d->d1 == d->d2 && d->obj != -1) {
+ d->tok = tok - 2;
+ if (d->tokptr) *d->tokptr = s + d->obj;
+ if (d->toklen) *d->toklen = off - d->obj + 1;
+ return 1;
+ }
+ } else if (tok == ',' && d->d1 == d->d2 && d->pos &&
+ d->path[d->pos - 1] == ']') {
+ return 1; // Not found in the current elem array
+ } else if (tok == ',' && d->d1 == d->d2 + 1 && d->path[d->pos] == '[') {
+ // printf("GG '%s' '%s'\n", d->path, &d->path[d->pos]);
+ d->i1++;
+ if (d->i1 == d->i2) {
+ while (d->path[d->pos] && d->path[d->pos] != ']') d->pos++;
+ if (d->path[d->pos] == ']') d->pos++;
+ d->d2++;
+ }
+ } else if (tok == MJSON_TOK_KEY && d->d1 == d->d2 + 1 &&
+ d->path[d->pos] == '.' && s[off] == '"' &&
+ s[off + len - 1] == '"' &&
+ plen1(&d->path[d->pos + 1]) == len - 2 &&
+ kcmp(s + off + 1, &d->path[d->pos + 1], len - 2) == 0) {
+ d->d2++;
+ d->pos += plen2(&d->path[d->pos + 1]) + 1;
+ } else if (tok == MJSON_TOK_KEY && d->d1 == d->d2) {
+ return 1; // Exhausted path, not found
+ } else if (MJSON_TOK_IS_VALUE(tok)) {
+ // printf("T %d %d %d %d %d\n", tok, d->d1, d->d2, d->i1, d->i2);
+ if (d->d1 == d->d2 && d->i1 == d->i2 && !d->path[d->pos]) {
+ d->tok = tok;
+ if (d->tokptr) *d->tokptr = s + off;
+ if (d->toklen) *d->toklen = len;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int mjson_find(const char *s, int n, const char *jp, const char **tp, int *tl) {
+ struct msjon_get_data data = {jp, 1, 0, 0, 0,
+ 0, -1, tp, tl, MJSON_TOK_INVALID};
+ if (jp[0] != '$') return MJSON_TOK_INVALID;
+ if (mjson(s, n, mjson_get_cb, &data) < 0) return MJSON_TOK_INVALID;
+ return data.tok;
+}
+
+int mjson_get_number(const char *s, int len, const char *path, double *v) {
+ const char *p;
+ int tok, n;
+ if ((tok = mjson_find(s, len, path, &p, &n)) == MJSON_TOK_NUMBER) {
+ if (v != NULL) *v = mystrtod(p, NULL);
+ }
+ return tok == MJSON_TOK_NUMBER ? 1 : 0;
+}
+
+int mjson_get_bool(const char *s, int len, const char *path, int *v) {
+ int tok = mjson_find(s, len, path, NULL, NULL);
+ if (tok == MJSON_TOK_TRUE && v != NULL) *v = 1;
+ if (tok == MJSON_TOK_FALSE && v != NULL) *v = 0;
+ return tok == MJSON_TOK_TRUE || tok == MJSON_TOK_FALSE ? 1 : 0;
+}
+
+static unsigned char unhex(unsigned char c) {
+ return (c >= '0' && c <= '9') ? (unsigned char) (c - '0')
+ : (c >= 'A' && c <= 'F') ? (unsigned char) (c - '7')
+ : (unsigned char) (c - 'W');
+}
+
+static unsigned char mjson_unhex_nimble(const char *s) {
+ const unsigned char *u = (const unsigned char *) s;
+ return (unsigned char) (((unsigned char) (unhex(u[0]) << 4)) | unhex(u[1]));
+}
+
+static int mjson_unescape(const char *s, int len, char *to, int n) {
+ int i, j;
+ for (i = 0, j = 0; i < len && j < n; i++, j++) {
+ if (s[i] == '\\' && i + 5 < len && s[i + 1] == 'u') {
+ // \uXXXX escape. We could process a simple one-byte chars
+ // \u00xx from the ASCII range. More complex chars would require
+ // dragging in a UTF8 library, which is too much for us
+ if (s[i + 2] != '0' || s[i + 3] != '0') return -1; // Too much, give up
+ ((unsigned char *) to)[j] = mjson_unhex_nimble(s + i + 4);
+ i += 5;
+ } else if (s[i] == '\\' && i + 1 < len) {
+ int c = mjson_esc(s[i + 1], 0);
+ if (c == 0) return -1;
+ to[j] = (char) (unsigned char) c;
+ i++;
+ } else {
+ to[j] = s[i];
+ }
+ }
+ if (j >= n) return -1;
+ if (n > 0) to[j] = '\0';
+ return j;
+}
+
+int mjson_get_string(const char *s, int len, const char *path, char *to,
+ int n) {
+ const char *p;
+ int sz;
+ if (mjson_find(s, len, path, &p, &sz) != MJSON_TOK_STRING) return -1;
+ return mjson_unescape(p + 1, sz - 2, to, n);
+}
+
+int mjson_get_hex(const char *s, int len, const char *x, char *to, int n) {
+ const char *p;
+ int i, j, sz;
+ if (mjson_find(s, len, x, &p, &sz) != MJSON_TOK_STRING) return -1;
+ for (i = j = 0; i < sz - 3 && j < n; i += 2, j++) {
+ ((unsigned char *) to)[j] = mjson_unhex_nimble(p + i + 1);
+ }
+ if (j < n) to[j] = '\0';
+ return j;
+}
+
+#if MJSON_ENABLE_BASE64
+static unsigned char mjson_base64rev(int c) {
+ if (c >= 'A' && c <= 'Z') {
+ return (unsigned char) (c - 'A');
+ } else if (c >= 'a' && c <= 'z') {
+ return (unsigned char) (c + 26 - 'a');
+ } else if (c >= '0' && c <= '9') {
+ return (unsigned char) (c + 52 - '0');
+ } else if (c == '+') {
+ return 62;
+ } else if (c == '/') {
+ return 63;
+ } else {
+ return 64;
+ }
+}
+
+int mjson_base64_dec(const char *src, int n, char *dst, int dlen) {
+ const char *end = src + n;
+ int len = 0;
+ while (src + 3 < end && len < dlen) {
+ unsigned char a = mjson_base64rev(src[0]), b = mjson_base64rev(src[1]),
+ c = mjson_base64rev(src[2]), d = mjson_base64rev(src[3]);
+ dst[len++] = (char) (unsigned char) ((a << 2) | (b >> 4));
+ if (src[2] != '=' && len < dlen) {
+ dst[len++] = (char) (unsigned char) ((b << 4) | (c >> 2));
+ if (src[3] != '=' && len < dlen) {
+ dst[len++] = (char) (unsigned char) ((c << 6) | d);
+ }
+ }
+ src += 4;
+ }
+ if (len < dlen) dst[len] = '\0';
+ return len;
+}
+
+int mjson_get_base64(const char *s, int len, const char *path, char *to,
+ int n) {
+ const char *p;
+ int sz;
+ if (mjson_find(s, len, path, &p, &sz) != MJSON_TOK_STRING) return 0;
+ return mjson_base64_dec(p + 1, sz - 2, to, n);
+}
+#endif // MJSON_ENABLE_BASE64
+
+#if MJSON_ENABLE_NEXT
+struct nextdata {
+ int off, len, depth, t, vo, arrayindex;
+ int *koff, *klen, *voff, *vlen, *vtype;
+};
+
+static int next_cb(int tok, const char *s, int off, int len, void *ud) {
+ struct nextdata *d = (struct nextdata *) ud;
+ // int i;
+ switch (tok) {
+ case '{':
+ case '[':
+ if (d->depth == 0 && tok == '[') d->arrayindex = 0;
+ if (d->depth == 1 && off > d->off) {
+ d->vo = off;
+ d->t = tok == '{' ? MJSON_TOK_OBJECT : MJSON_TOK_ARRAY;
+ if (d->voff) *d->voff = off;
+ if (d->vtype) *d->vtype = d->t;
+ }
+ d->depth++;
+ break;
+ case '}':
+ case ']':
+ d->depth--;
+ if (d->depth == 1 && d->vo) {
+ d->len = off + len;
+ if (d->vlen) *d->vlen = d->len - d->vo;
+ if (d->arrayindex >= 0) {
+ if (d->koff) *d->koff = d->arrayindex; // koff holds array index
+ if (d->klen) *d->klen = 0; // klen holds 0
+ }
+ return 1;
+ }
+ if (d->depth == 1 && d->arrayindex >= 0) d->arrayindex++;
+ break;
+ case ',':
+ case ':':
+ break;
+ case MJSON_TOK_KEY:
+ if (d->depth == 1 && d->off < off) {
+ if (d->koff) *d->koff = off; // And report back to the user
+ if (d->klen) *d->klen = len; // If we have to
+ }
+ break;
+ default:
+ if (d->depth != 1) break;
+ // If we're iterating over the array
+ if (off > d->off) {
+ d->len = off + len;
+ if (d->vlen) *d->vlen = len; // value length
+ if (d->voff) *d->voff = off; // value offset
+ if (d->vtype) *d->vtype = tok; // value type
+ if (d->arrayindex >= 0) {
+ if (d->koff) *d->koff = d->arrayindex; // koff holds array index
+ if (d->klen) *d->klen = 0; // klen holds 0
+ }
+ return 1;
+ }
+ if (d->arrayindex >= 0) d->arrayindex++;
+ break;
+ }
+ (void) s;
+ return 0;
+}
+
+int mjson_next(const char *s, int n, int off, int *koff, int *klen, int *voff,
+ int *vlen, int *vtype) {
+ struct nextdata d = {off, 0, 0, 0, 0, -1, koff, klen, voff, vlen, vtype};
+ mjson(s, n, next_cb, &d);
+ return d.len;
+}
+#endif
+
+#if MJSON_ENABLE_PRINT
+int mjson_print_fixed_buf(const char *ptr, int len, void *fn_data) {
+ struct mjson_fixedbuf *fb = (struct mjson_fixedbuf *) fn_data;
+ int i, left = fb->size - 1 - fb->len;
+ if (left < len) len = left;
+ for (i = 0; i < len; i++) fb->ptr[fb->len + i] = ptr[i];
+ fb->len += len;
+ fb->ptr[fb->len] = '\0';
+ return len;
+}
+
+// This function allocates memory in chunks of size MJSON_DYNBUF_CHUNK
+// to decrease memory fragmentation, when many calls are executed to
+// print e.g. a base64 string or a hex string.
+int mjson_print_dynamic_buf(const char *ptr, int len, void *fn_data) {
+ char *s, *buf = *(char **) fn_data;
+ size_t curlen = buf == NULL ? 0 : strlen(buf);
+ size_t new_size = curlen + (size_t) len + 1 + MJSON_DYNBUF_CHUNK;
+ new_size -= new_size % MJSON_DYNBUF_CHUNK;
+
+ if ((s = (char *) MJSON_REALLOC(buf, new_size)) == NULL) {
+ return 0;
+ } else {
+ memcpy(s + curlen, ptr, (size_t) len);
+ s[curlen + (size_t) len] = '\0';
+ *(char **) fn_data = s;
+ return len;
+ }
+}
+
+int mjson_snprintf(char *buf, size_t len, const char *fmt, ...) {
+ va_list ap;
+ struct mjson_fixedbuf fb = {buf, (int) len, 0};
+ va_start(ap, fmt);
+ mjson_vprintf(mjson_print_fixed_buf, &fb, fmt, &ap);
+ va_end(ap);
+ return fb.len;
+}
+
+char *mjson_aprintf(const char *fmt, ...) {
+ va_list ap;
+ char *result = NULL;
+ va_start(ap, fmt);
+ mjson_vprintf(mjson_print_dynamic_buf, &result, fmt, &ap);
+ va_end(ap);
+ return result;
+}
+
+int mjson_print_null(const char *ptr, int len, void *userdata) {
+ (void) ptr;
+ (void) userdata;
+ return len;
+}
+
+int mjson_print_buf(mjson_print_fn_t fn, void *fnd, const char *buf, int len) {
+ return fn(buf, len, fnd);
+}
+
+int mjson_print_long(mjson_print_fn_t fn, void *fnd, long val, int is_signed) {
+ unsigned long v = (unsigned long) val, s = 0, n, i;
+ char buf[20], t;
+ if (is_signed && val < 0) buf[s++] = '-', v = (unsigned long) (-val);
+ // This loop prints a number in reverse order. I guess this is because we
+ // write numbers from right to left: least significant digit comes last.
+ // Maybe because we use Arabic numbers, and Arabs write RTL?
+ for (n = 0; v > 0; v /= 10) buf[s + n++] = "0123456789"[v % 10];
+ // Reverse a string
+ for (i = 0; i < n / 2; i++)
+ t = buf[s + i], buf[s + i] = buf[s + n - i - 1], buf[s + n - i - 1] = t;
+ if (val == 0) buf[n++] = '0'; // Handle special case
+ return fn(buf, (int) (s + n), fnd);
+}
+
+int mjson_print_int(mjson_print_fn_t fn, void *fnd, int v, int s) {
+ return mjson_print_long(fn, fnd, s ? (long) v : (long) (unsigned) v, s);
+}
+
+static int addexp(char *buf, int e, int sign) {
+ int n = 0;
+ buf[n++] = 'e';
+ buf[n++] = (char) sign;
+ if (e > 400) return 0;
+ if (e < 10) buf[n++] = '0';
+ if (e >= 100) buf[n++] = (char) (e / 100 + '0'), e -= 100 * (e / 100);
+ if (e >= 10) buf[n++] = (char) (e / 10 + '0'), e -= 10 * (e / 10);
+ buf[n++] = (char) (e + '0');
+ return n;
+}
+
+int mjson_print_dbl(mjson_print_fn_t fn, void *fnd, double d, int width) {
+ char buf[40];
+ int i, s = 0, n = 0, e = 0;
+ double t, mul, saved;
+ if (d == 0.0) return fn("0", 1, fnd);
+ if (isinf(d)) return fn(d > 0 ? "inf" : "-inf", d > 0 ? 3 : 4, fnd);
+ if (isnan(d)) return fn("nan", 3, fnd);
+ if (d < 0.0) d = -d, buf[s++] = '-';
+
+ // Round
+ saved = d;
+ mul = 1.0;
+ while (d >= 10.0 && d / mul >= 10.0) mul *= 10.0;
+ while (d <= 1.0 && d / mul <= 1.0) mul /= 10.0;
+ for (i = 0, t = mul * 5; i < width; i++) t /= 10.0;
+ d += t;
+ // Calculate exponent, and 'mul' for scientific representation
+ mul = 1.0;
+ while (d >= 10.0 && d / mul >= 10.0) mul *= 10.0, e++;
+ while (d < 1.0 && d / mul < 1.0) mul /= 10.0, e--;
+ // printf(" --> %g %d %g %g\n", saved, e, t, mul);
+
+ if (e >= width) {
+ struct mjson_fixedbuf fb = {buf + s, (int) sizeof(buf) - s, 0};
+ n = mjson_print_dbl(mjson_print_fixed_buf, &fb, saved / mul, width);
+ // printf(" --> %.*g %d [%.*s]\n", 10, d / t, e, fb.len, fb.ptr);
+ n += addexp(buf + s + n, e, '+');
+ return fn(buf, s + n, fnd);
+ } else if (e <= -width) {
+ struct mjson_fixedbuf fb = {buf + s, (int) sizeof(buf) - s, 0};
+ n = mjson_print_dbl(mjson_print_fixed_buf, &fb, saved / mul, width);
+ // printf(" --> %.*g %d [%.*s]\n", 10, d / mul, e, fb.len, fb.ptr);
+ n += addexp(buf + s + n, -e, '-');
+ return fn(buf, s + n, fnd);
+ } else {
+ for (i = 0, t = mul; t >= 1.0 && s + n < (int) sizeof(buf); i++) {
+ int ch = (int) (d / t);
+ if (n > 0 || ch > 0) buf[s + n++] = (char) (ch + '0');
+ d -= ch * t;
+ t /= 10.0;
+ }
+ // printf(" --> [%g] -> %g %g (%d) [%.*s]\n", saved, d, t, n, s + n, buf);
+ if (n == 0) buf[s++] = '0';
+ while (t >= 1.0 && n + s < (int) sizeof(buf)) buf[n++] = '0', t /= 10.0;
+ if (s + n < (int) sizeof(buf)) buf[n + s++] = '.';
+ // printf(" 1--> [%g] -> [%.*s]\n", saved, s + n, buf);
+ for (i = 0, t = 0.1; s + n < (int) sizeof(buf) && n < width; i++) {
+ int ch = (int) (d / t);
+ buf[s + n++] = (char) (ch + '0');
+ d -= ch * t;
+ t /= 10.0;
+ }
+ }
+ while (n > 0 && buf[s + n - 1] == '0') n--; // Trim trailing zeros
+ if (n > 0 && buf[s + n - 1] == '.') n--; // Trim trailing dot
+ buf[s + n] = '\0';
+ return fn(buf, s + n, fnd);
+}
+
+int mjson_print_str(mjson_print_fn_t fn, void *fnd, const char *s, int len) {
+ int i, n = fn("\"", 1, fnd);
+ for (i = 0; i < len; i++) {
+ char c = (char) (unsigned char) mjson_escape(s[i]);
+ if (c) {
+ n += fn("\\", 1, fnd);
+ n += fn(&c, 1, fnd);
+ } else {
+ n += fn(&s[i], 1, fnd);
+ }
+ }
+ return n + fn("\"", 1, fnd);
+}
+
+#if MJSON_ENABLE_BASE64
+int mjson_print_b64(mjson_print_fn_t fn, void *fnd, const unsigned char *s,
+ int n) {
+ const char *t =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ int i, len = fn("\"", 1, fnd);
+ for (i = 0; i < n; i += 3) {
+ int a = s[i], b = i + 1 < n ? s[i + 1] : 0, c = i + 2 < n ? s[i + 2] : 0;
+ char buf[4] = {t[a >> 2], t[(a & 3) << 4 | (b >> 4)], '=', '='};
+ if (i + 1 < n) buf[2] = t[(b & 15) << 2 | (c >> 6)];
+ if (i + 2 < n) buf[3] = t[c & 63];
+ len += fn(buf, sizeof(buf), fnd);
+ }
+ return len + fn("\"", 1, fnd);
+}
+#endif /* MJSON_ENABLE_BASE64 */
+
+int mjson_vprintf(mjson_print_fn_t fn, void *fnd, const char *fmt,
+ va_list *ap) {
+ int i = 0, n = 0;
+ while (fmt[i] != '\0') {
+ if (fmt[i] == '%') {
+ char fc = fmt[++i];
+ int is_long = 0;
+ if (fc == 'l') {
+ is_long = 1;
+ fc = fmt[i + 1];
+ }
+ if (fc == 'Q') {
+ char *buf = va_arg(*ap, char *);
+ n += mjson_print_str(fn, fnd, buf ? buf : "",
+ buf ? (int) strlen(buf) : 0);
+ } else if (strncmp(&fmt[i], ".*Q", 3) == 0) {
+ int len = va_arg(*ap, int);
+ char *buf = va_arg(*ap, char *);
+ n += mjson_print_str(fn, fnd, buf, len);
+ i += 2;
+ } else if (fc == 'd' || fc == 'u') {
+ int is_signed = (fc == 'd');
+ if (is_long) {
+ long val = va_arg(*ap, long);
+ n += mjson_print_long(fn, fnd, val, is_signed);
+ i++;
+ } else {
+ int val = va_arg(*ap, int);
+ n += mjson_print_int(fn, fnd, val, is_signed);
+ }
+ } else if (fc == 'B') {
+ const char *s = va_arg(*ap, int) ? "true" : "false";
+ n += mjson_print_buf(fn, fnd, s, (int) strlen(s));
+ } else if (fc == 's') {
+ char *buf = va_arg(*ap, char *);
+ n += mjson_print_buf(fn, fnd, buf, (int) strlen(buf));
+ } else if (strncmp(&fmt[i], ".*s", 3) == 0) {
+ int len = va_arg(*ap, int);
+ char *buf = va_arg(*ap, char *);
+ n += mjson_print_buf(fn, fnd, buf, len);
+ i += 2;
+ } else if (fc == 'g') {
+ n += mjson_print_dbl(fn, fnd, va_arg(*ap, double), 6);
+ } else if (strncmp(&fmt[i], ".*g", 3) == 0) {
+ int width = va_arg(*ap, int);
+ n += mjson_print_dbl(fn, fnd, va_arg(*ap, double), width);
+ i += 2;
+#if MJSON_ENABLE_BASE64
+ } else if (fc == 'V') {
+ int len = va_arg(*ap, int);
+ const char *buf = va_arg(*ap, const char *);
+ n += mjson_print_b64(fn, fnd, (const unsigned char *) buf, len);
+#endif
+ } else if (fc == 'H') {
+ const char *hex = "0123456789abcdef";
+ int j, len = va_arg(*ap, int);
+ const unsigned char *p = va_arg(*ap, const unsigned char *);
+ n += fn("\"", 1, fnd);
+ for (j = 0; j < len; j++) {
+ n += fn(&hex[(p[j] >> 4) & 15], 1, fnd);
+ n += fn(&hex[p[j] & 15], 1, fnd);
+ }
+ n += fn("\"", 1, fnd);
+ } else if (fc == 'M') {
+ mjson_vprint_fn_t vfn = va_arg(*ap, mjson_vprint_fn_t);
+ n += vfn(fn, fnd, ap);
+ }
+ i++;
+ } else {
+ n += mjson_print_buf(fn, fnd, &fmt[i++], 1);
+ }
+ }
+ return n;
+}
+
+int mjson_printf(mjson_print_fn_t fn, void *fnd, const char *fmt, ...) {
+ va_list ap;
+ int len;
+ va_start(ap, fmt);
+ len = mjson_vprintf(fn, fnd, fmt, &ap);
+ va_end(ap);
+ return len;
+}
+#endif /* MJSON_ENABLE_PRINT */
+
+static int is_digit(int c) {
+ return c >= '0' && c <= '9';
+}
+
+/* NOTE: strtod() implementation by Yasuhiro Matsumoto. */
+static double mystrtod(const char *str, const char **end) {
+ double d = 0.0;
+ int sign = 1;
+ const char *p = str, *a = str;
+
+ /* decimal part */
+ if (*p == '-') {
+ sign = -1;
+ ++p;
+ } else if (*p == '+') {
+ ++p;
+ }
+ if (is_digit(*p)) {
+ d = (double) (*p++ - '0');
+ while (*p && is_digit(*p)) {
+ d = d * 10.0 + (double) (*p - '0');
+ ++p;
+ }
+ a = p;
+ } else if (*p != '.') {
+ goto done;
+ }
+ d *= sign;
+
+ /* fraction part */
+ if (*p == '.') {
+ double f = 0.0;
+ double base = 0.1;
+ ++p;
+
+ if (is_digit(*p)) {
+ while (*p && is_digit(*p)) {
+ f += base * (*p - '0');
+ base /= 10.0;
+ ++p;
+ }
+ }
+ d += f * sign;
+ a = p;
+ }
+
+ /* exponential part */
+ if ((*p == 'E') || (*p == 'e')) {
+ int i, e = 0, neg = 0;
+ p++;
+ if (*p == '-') p++, neg++;
+ if (*p == '+') p++;
+ while (is_digit(*p)) e = e * 10 + *p++ - '0';
+ if (neg) e = -e;
+#if 0
+ if (d == 2.2250738585072011 && e == -308) {
+ d = 0.0;
+ a = p;
+ goto done;
+ }
+ if (d == 2.2250738585072012 && e <= -308) {
+ d *= 1.0e-308;
+ a = p;
+ goto done;
+ }
+#endif
+ for (i = 0; i < e; i++) d *= 10;
+ for (i = 0; i < -e; i++) d /= 10;
+ a = p;
+ } else if (p > str && !is_digit(*(p - 1))) {
+ a = str;
+ goto done;
+ }
+
+done:
+ if (end) *end = a;
+ return d;
+}
+
+#if MJSON_ENABLE_MERGE
+int mjson_merge(const char *s, int n, const char *s2, int n2,
+ mjson_print_fn_t fn, void *userdata) {
+ int koff, klen, voff, vlen, t, t2, k, off = 0, len = 0, comma = 0;
+ if (n < 2) return len;
+ len += fn("{", 1, userdata);
+ while ((off = mjson_next(s, n, off, &koff, &klen, &voff, &vlen, &t)) != 0) {
+ char *path = (char *) alloca((size_t) klen + 1);
+ const char *val;
+ memcpy(path, "$.", 2);
+ memcpy(path + 2, s + koff + 1, (size_t) (klen - 2));
+ path[klen] = '\0';
+ if ((t2 = mjson_find(s2, n2, path, &val, &k)) != MJSON_TOK_INVALID) {
+ if (t2 == MJSON_TOK_NULL) continue; // null deletes the key
+ } else {
+ val = s + voff; // Key is not found in the update. Copy the old value.
+ }
+ if (comma) len += fn(",", 1, userdata);
+ len += fn(s + koff, klen, userdata);
+ len += fn(":", 1, userdata);
+ if (t == MJSON_TOK_OBJECT && t2 == MJSON_TOK_OBJECT) {
+ len += mjson_merge(s + voff, vlen, val, k, fn, userdata);
+ } else {
+ if (t2 != MJSON_TOK_INVALID) vlen = k;
+ len += fn(val, vlen, userdata);
+ }
+ comma = 1;
+ }
+ // Add missing keys
+ off = 0;
+ while ((off = mjson_next(s2, n2, off, &koff, &klen, &voff, &vlen, &t)) != 0) {
+ char *path = (char *) alloca((size_t) klen + 1);
+ const char *val;
+ if (t == MJSON_TOK_NULL) continue;
+ memcpy(path, "$.", 2);
+ memcpy(path + 2, s2 + koff + 1, (size_t) (klen - 2));
+ path[klen] = '\0';
+ if (mjson_find(s, n, path, &val, &vlen) != MJSON_TOK_INVALID) continue;
+ if (comma) len += fn(",", 1, userdata);
+ len += fn(s2 + koff, klen, userdata);
+ len += fn(":", 1, userdata);
+ len += fn(s2 + voff, vlen, userdata);
+ comma = 1;
+ }
+ len += fn("}", 1, userdata);
+ return len;
+}
+#endif // MJSON_ENABLE_MERGE
+
+#if MJSON_ENABLE_PRETTY
+struct prettydata {
+ int level;
+ int len;
+ int prev;
+ const char *pad;
+ int padlen;
+ mjson_print_fn_t fn;
+ void *userdata;
+};
+
+static int pretty_cb(int ev, const char *s, int off, int len, void *ud) {
+ struct prettydata *d = (struct prettydata *) ud;
+ int i;
+ switch (ev) {
+ case '{':
+ case '[':
+ d->level++;
+ d->len += d->fn(s + off, len, d->userdata);
+ break;
+ case '}':
+ case ']':
+ d->level--;
+ if (d->prev != '[' && d->prev != '{' && d->padlen > 0) {
+ d->len += d->fn("\n", 1, d->userdata);
+ for (i = 0; i < d->level; i++)
+ d->len += d->fn(d->pad, d->padlen, d->userdata);
+ }
+ d->len += d->fn(s + off, len, d->userdata);
+ break;
+ case ',':
+ d->len += d->fn(s + off, len, d->userdata);
+ if (d->padlen > 0) {
+ d->len += d->fn("\n", 1, d->userdata);
+ for (i = 0; i < d->level; i++)
+ d->len += d->fn(d->pad, d->padlen, d->userdata);
+ }
+ break;
+ case ':':
+ d->len += d->fn(s + off, len, d->userdata);
+ if (d->padlen > 0) d->len += d->fn(" ", 1, d->userdata);
+ break;
+ case MJSON_TOK_KEY:
+ if (d->prev == '{' && d->padlen > 0) {
+ d->len += d->fn("\n", 1, d->userdata);
+ for (i = 0; i < d->level; i++)
+ d->len += d->fn(d->pad, d->padlen, d->userdata);
+ }
+ d->len += d->fn(s + off, len, d->userdata);
+ break;
+ default:
+ if (d->prev == '[' && d->padlen > 0) {
+ d->len += d->fn("\n", 1, d->userdata);
+ for (i = 0; i < d->level; i++)
+ d->len += d->fn(d->pad, d->padlen, d->userdata);
+ }
+ d->len += d->fn(s + off, len, d->userdata);
+ break;
+ }
+ d->prev = ev;
+ return 0;
+}
+
+int mjson_pretty(const char *s, int n, const char *pad, mjson_print_fn_t fn,
+ void *userdata) {
+ struct prettydata d = {0, 0, 0, pad, (int) strlen(pad), fn, userdata};
+ if (mjson(s, n, pretty_cb, &d) < 0) return -1;
+ return d.len;
+}
+#endif // MJSON_ENABLE_PRETTY
+
+#if MJSON_ENABLE_RPC
+struct jsonrpc_ctx jsonrpc_default_context;
+
+int mjson_globmatch(const char *s1, int n1, const char *s2, int n2) {
+ int i = 0, j = 0, ni = 0, nj = 0;
+ while (i < n1 || j < n2) {
+ if (i < n1 && j < n2 && (s1[i] == '?' || s2[j] == s1[i])) {
+ i++, j++;
+ } else if (i < n1 && (s1[i] == '*' || s1[i] == '#')) {
+ ni = i, nj = j + 1, i++;
+ } else if (nj > 0 && nj <= n2 && (s1[i - 1] == '#' || s2[j] != '/')) {
+ i = ni, j = nj;
+ } else {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+void jsonrpc_return_errorv(struct jsonrpc_request *r, int code,
+ const char *message, const char *data_fmt,
+ va_list *ap) {
+ if (r->id_len == 0) return;
+ mjson_printf(r->fn, r->fn_data,
+ "{\"id\":%.*s,\"error\":{\"code\":%d,\"message\":%Q", r->id_len,
+ r->id, code, message == NULL ? "" : message);
+ if (data_fmt != NULL) {
+ mjson_printf(r->fn, r->fn_data, ",\"data\":");
+ mjson_vprintf(r->fn, r->fn_data, data_fmt, ap);
+ }
+ mjson_printf(r->fn, r->fn_data, "}}\n");
+}
+
+void jsonrpc_return_error(struct jsonrpc_request *r, int code,
+ const char *message, const char *data_fmt, ...) {
+ va_list ap;
+ va_start(ap, data_fmt);
+ jsonrpc_return_errorv(r, code, message, data_fmt, &ap);
+ va_end(ap);
+}
+
+void jsonrpc_return_successv(struct jsonrpc_request *r, const char *result_fmt,
+ va_list *ap) {
+ if (r->id_len == 0) return;
+ mjson_printf(r->fn, r->fn_data, "{\"id\":%.*s,\"result\":", r->id_len, r->id);
+ if (result_fmt != NULL) {
+ mjson_vprintf(r->fn, r->fn_data, result_fmt, ap);
+ } else {
+ mjson_printf(r->fn, r->fn_data, "%s", "null");
+ }
+ mjson_printf(r->fn, r->fn_data, "}\n");
+}
+
+void jsonrpc_return_success(struct jsonrpc_request *r, const char *result_fmt,
+ ...) {
+ va_list ap;
+ va_start(ap, result_fmt);
+ jsonrpc_return_successv(r, result_fmt, &ap);
+ va_end(ap);
+}
+
+void jsonrpc_ctx_process(struct jsonrpc_ctx *ctx, const char *buf, int len,
+ mjson_print_fn_t fn, void *fn_data, void *ud) {
+ const char *result = NULL, *error = NULL;
+ int result_sz = 0, error_sz = 0;
+ struct jsonrpc_method *m = NULL;
+ struct jsonrpc_request r = {ctx, buf, len, 0, 0, 0, 0, 0, 0, fn, fn_data, ud};
+
+ // Is is a response frame?
+ mjson_find(buf, len, "$.result", &result, &result_sz);
+ if (result == NULL) mjson_find(buf, len, "$.error", &error, &error_sz);
+ if (result_sz > 0 || error_sz > 0) {
+ if (ctx->response_cb) ctx->response_cb(buf, len, ctx->response_cb_data);
+ return;
+ }
+
+ // Method must exist and must be a string
+ if (mjson_find(buf, len, "$.method", &r.method, &r.method_len) !=
+ MJSON_TOK_STRING) {
+ mjson_printf(fn, fn_data,
+ "{\"error\":{\"code\":-32700,\"message\":%.*Q}}\n", len, buf);
+ return;
+ }
+
+ // id and params are optional
+ mjson_find(buf, len, "$.id", &r.id, &r.id_len);
+ mjson_find(buf, len, "$.params", &r.params, &r.params_len);
+
+ for (m = ctx->methods; m != NULL; m = m->next) {
+ if (mjson_globmatch(m->method, m->method_sz, r.method + 1,
+ r.method_len - 2) > 0) {
+ if (r.params == NULL) r.params = "";
+ m->cb(&r);
+ break;
+ }
+ }
+ if (m == NULL) {
+ jsonrpc_return_error(&r, JSONRPC_ERROR_NOT_FOUND, "method not found", NULL);
+ }
+}
+
+static int jsonrpc_print_methods(mjson_print_fn_t fn, void *fn_data,
+ va_list *ap) {
+ struct jsonrpc_ctx *ctx = va_arg(*ap, struct jsonrpc_ctx *);
+ struct jsonrpc_method *m;
+ int len = 0;
+ for (m = ctx->methods; m != NULL; m = m->next) {
+ if (m != ctx->methods) len += mjson_print_buf(fn, fn_data, ",", 1);
+ len += mjson_print_str(fn, fn_data, m->method, (int) strlen(m->method));
+ }
+ return len;
+}
+
+void jsonrpc_list(struct jsonrpc_request *r) {
+ jsonrpc_return_success(r, "[%M]", jsonrpc_print_methods, r->ctx);
+}
+
+void jsonrpc_ctx_init(struct jsonrpc_ctx *ctx, mjson_print_fn_t response_cb,
+ void *response_cb_data) {
+ ctx->methods = NULL;
+ ctx->response_cb = response_cb;
+ ctx->response_cb_data = response_cb_data;
+}
+
+void jsonrpc_init(mjson_print_fn_t response_cb, void *userdata) {
+ struct jsonrpc_ctx *ctx = &jsonrpc_default_context;
+ jsonrpc_ctx_init(ctx, response_cb, userdata);
+ jsonrpc_ctx_export(ctx, MJSON_RPC_LIST_NAME, jsonrpc_list);
+}
+#endif // MJSON_ENABLE_RPC
--- /dev/null
+// Copyright (c) 2018-2020 Cesanta Software Limited
+// All rights reserved
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+#ifndef MJSON_H
+#define MJSON_H
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef MJSON_ENABLE_PRINT
+#define MJSON_ENABLE_PRINT 1
+#endif
+
+#ifndef MJSON_ENABLE_RPC
+#define MJSON_ENABLE_RPC 1
+#endif
+
+#ifndef MJSON_ENABLE_BASE64
+#define MJSON_ENABLE_BASE64 1
+#endif
+
+#ifndef MJSON_ENABLE_MERGE
+#define MJSON_ENABLE_MERGE 1
+#endif
+
+#ifndef MJSON_ENABLE_PRETTY
+#define MJSON_ENABLE_PRETTY 1
+#endif
+
+#ifndef MJSON_ENABLE_NEXT
+#define MJSON_ENABLE_NEXT 1
+#endif
+
+#ifndef MJSON_RPC_LIST_NAME
+#define MJSON_RPC_LIST_NAME "rpc.list"
+#endif
+
+#ifndef MJSON_DYNBUF_CHUNK
+#define MJSON_DYNBUF_CHUNK 256 // Allocation granularity for print_dynamic_buf
+#endif
+
+#ifndef MJSON_REALLOC
+#define MJSON_REALLOC realloc
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MJSON_ERROR_INVALID_INPUT (-1)
+#define MJSON_ERROR_TOO_DEEP (-2)
+#define MJSON_TOK_INVALID 0
+#define MJSON_TOK_KEY 1
+#define MJSON_TOK_STRING 11
+#define MJSON_TOK_NUMBER 12
+#define MJSON_TOK_TRUE 13
+#define MJSON_TOK_FALSE 14
+#define MJSON_TOK_NULL 15
+#define MJSON_TOK_ARRAY 91
+#define MJSON_TOK_OBJECT 123
+#define MJSON_TOK_IS_VALUE(t) ((t) > 10 && (t) < 20)
+
+typedef int (*mjson_cb_t)(int event, const char *buf, int offset, int len,
+ void *fn_data);
+
+#ifndef MJSON_MAX_DEPTH
+#define MJSON_MAX_DEPTH 20
+#endif
+
+int mjson(const char *buf, int len, mjson_cb_t cb, void *ud);
+int mjson_find(const char *buf, int len, const char *jp, const char **tp,
+ int *tl);
+int mjson_get_number(const char *buf, int len, const char *path, double *v);
+int mjson_get_bool(const char *buf, int len, const char *path, int *v);
+int mjson_get_string(const char *buf, int len, const char *path, char *to,
+ int n);
+int mjson_get_hex(const char *buf, int len, const char *path, char *to, int n);
+
+#if MJSON_ENABLE_NEXT
+int mjson_next(const char *buf, int len, int offset, int *key_offset,
+ int *key_len, int *val_offset, int *val_len, int *vale_type);
+#endif
+
+#if MJSON_ENABLE_BASE64
+int mjson_get_base64(const char *buf, int len, const char *path, char *dst,
+ int dst_len);
+int mjson_base64_dec(const char *src, int src_len, char *dst, int dst_len);
+#endif
+
+#if MJSON_ENABLE_PRINT
+typedef int (*mjson_print_fn_t)(const char *buf, int len, void *fn_data);
+typedef int (*mjson_vprint_fn_t)(mjson_print_fn_t fn, void *, va_list *);
+
+struct mjson_fixedbuf {
+ char *ptr;
+ int size, len;
+};
+
+int mjson_printf(mjson_print_fn_t fn, void *fn_data, const char *fmt, ...);
+int mjson_vprintf(mjson_print_fn_t fn, void *fn_data, const char *fmt,
+ va_list *ap);
+int mjson_print_str(mjson_print_fn_t fn, void *fn_data, const char *buf,
+ int len);
+int mjson_print_int(mjson_print_fn_t fn, void *fn_data, int value,
+ int is_signed);
+int mjson_print_long(mjson_print_fn_t fn, void *fn_data, long value,
+ int is_signed);
+int mjson_print_buf(mjson_print_fn_t fn, void *fn_data, const char *buf,
+ int len);
+int mjson_print_dbl(mjson_print_fn_t fn, void *fn_data, double d, int width);
+
+int mjson_print_null(const char *ptr, int len, void *fn_data);
+int mjson_print_fixed_buf(const char *ptr, int len, void *fn_data);
+int mjson_print_dynamic_buf(const char *ptr, int len, void *fn_data);
+
+int mjson_snprintf(char *buf, size_t len, const char *fmt, ...);
+char *mjson_aprintf(const char *fmt, ...);
+
+#if MJSON_ENABLE_PRETTY
+int mjson_pretty(const char *s, int n, const char *pad, mjson_print_fn_t fn,
+ void *fn_data);
+#endif
+
+#if MJSON_ENABLE_MERGE
+int mjson_merge(const char *s, int n, const char *s2, int n2,
+ mjson_print_fn_t fn, void *fn_data);
+#endif
+
+#endif // MJSON_ENABLE_PRINT
+
+#if MJSON_ENABLE_RPC
+
+void jsonrpc_init(mjson_print_fn_t response_cb, void *fn_data);
+int mjson_globmatch(const char *s1, int n1, const char *s2, int n2);
+
+struct jsonrpc_request {
+ struct jsonrpc_ctx *ctx;
+ const char *frame; // Points to the whole frame
+ int frame_len; // Frame length
+ const char *params; // Points to the "params" in the request frame
+ int params_len; // Length of the "params"
+ const char *id; // Points to the "id" in the request frame
+ int id_len; // Length of the "id"
+ const char *method; // Points to the "method" in the request frame
+ int method_len; // Length of the "method"
+ mjson_print_fn_t fn; // Printer function
+ void *fn_data; // Printer function data
+ void *userdata; // Callback's user data as specified at export time
+};
+
+struct jsonrpc_method {
+ const char *method;
+ int method_sz;
+ void (*cb)(struct jsonrpc_request *);
+ struct jsonrpc_method *next;
+};
+
+// Main RPC context, stores current request information and a list of
+// exported RPC methods.
+struct jsonrpc_ctx {
+ struct jsonrpc_method *methods;
+ mjson_print_fn_t response_cb;
+ void *response_cb_data;
+};
+
+// Registers function fn under the given name within the given RPC context
+#define jsonrpc_ctx_export(ctx, name, fn) \
+ do { \
+ static struct jsonrpc_method m = {(name), sizeof(name) - 1, (fn), 0}; \
+ m.next = (ctx)->methods; \
+ (ctx)->methods = &m; \
+ } while (0)
+
+void jsonrpc_ctx_init(struct jsonrpc_ctx *ctx, mjson_print_fn_t response_cb,
+ void *response_cb_data);
+void jsonrpc_return_error(struct jsonrpc_request *r, int code,
+ const char *message, const char *data_fmt, ...);
+void jsonrpc_return_success(struct jsonrpc_request *r, const char *result_fmt,
+ ...);
+void jsonrpc_ctx_process(struct jsonrpc_ctx *ctx, const char *req, int req_sz,
+ mjson_print_fn_t fn, void *fn_data, void *userdata);
+
+extern struct jsonrpc_ctx jsonrpc_default_context;
+extern void jsonrpc_list(struct jsonrpc_request *r);
+
+#define jsonrpc_export(name, fn) \
+ jsonrpc_ctx_export(&jsonrpc_default_context, (name), (fn))
+
+#define jsonrpc_process(buf, len, fn, fnd, ud) \
+ jsonrpc_ctx_process(&jsonrpc_default_context, (buf), (len), (fn), (fnd), (ud))
+
+#define JSONRPC_ERROR_INVALID -32700 /* Invalid JSON was received */
+#define JSONRPC_ERROR_NOT_FOUND -32601 /* The method does not exist */
+#define JSONRPC_ERROR_BAD_PARAMS -32602 /* Invalid params passed */
+#define JSONRPC_ERROR_INTERNAL -32603 /* Internal JSON-RPC error */
+
+#endif // MJSON_ENABLE_RPC
+#ifdef __cplusplus
+}
+#endif
+#endif // MJSON_H
--- /dev/null
+// Copyright (c) 2004-2013 Sergey Lyubka
+// Copyright (c) 2013-2022 Cesanta Software Limited
+// All rights reserved
+//
+// This software is dual-licensed: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation. For the terms of this
+// license, see http://www.gnu.org/licenses/
+//
+// You are free to use this software under the terms of the GNU General
+// Public License, but WITHOUT ANY WARRANTY; without even the implied
+// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU General Public License for more details.
+//
+// Alternatively, you can license this software under a commercial
+// license, as set out in https://www.mongoose.ws/licensing/
+//
+// SPDX-License-Identifier: GPL-2.0-only or commercial
+
+#include "mongoose.h"
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/base64.c"
+#endif
+
+
+
+static int mg_b64idx(int c) {
+ if (c < 26) {
+ return c + 'A';
+ } else if (c < 52) {
+ return c - 26 + 'a';
+ } else if (c < 62) {
+ return c - 52 + '0';
+ } else {
+ return c == 62 ? '+' : '/';
+ }
+}
+
+static int mg_b64rev(int c) {
+ if (c >= 'A' && c <= 'Z') {
+ return c - 'A';
+ } else if (c >= 'a' && c <= 'z') {
+ return c + 26 - 'a';
+ } else if (c >= '0' && c <= '9') {
+ return c + 52 - '0';
+ } else if (c == '+') {
+ return 62;
+ } else if (c == '/') {
+ return 63;
+ } else if (c == '=') {
+ return 64;
+ } else {
+ return -1;
+ }
+}
+
+int mg_base64_update(unsigned char ch, char *to, int n) {
+ int rem = (n & 3) % 3;
+ if (rem == 0) {
+ to[n] = (char) mg_b64idx(ch >> 2);
+ to[++n] = (char) ((ch & 3) << 4);
+ } else if (rem == 1) {
+ to[n] = (char) mg_b64idx(to[n] | (ch >> 4));
+ to[++n] = (char) ((ch & 15) << 2);
+ } else {
+ to[n] = (char) mg_b64idx(to[n] | (ch >> 6));
+ to[++n] = (char) mg_b64idx(ch & 63);
+ n++;
+ }
+ return n;
+}
+
+int mg_base64_final(char *to, int n) {
+ int saved = n;
+ // printf("---[%.*s]\n", n, to);
+ if (n & 3) n = mg_base64_update(0, to, n);
+ if ((saved & 3) == 2) n--;
+ // printf(" %d[%.*s]\n", n, n, to);
+ while (n & 3) to[n++] = '=';
+ to[n] = '\0';
+ return n;
+}
+
+int mg_base64_encode(const unsigned char *p, int n, char *to) {
+ int i, len = 0;
+ for (i = 0; i < n; i++) len = mg_base64_update(p[i], to, len);
+ len = mg_base64_final(to, len);
+ return len;
+}
+
+int mg_base64_decode(const char *src, int n, char *dst) {
+ const char *end = src == NULL ? NULL : src + n; // Cannot add to NULL
+ int len = 0;
+ while (src != NULL && src + 3 < end) {
+ int a = mg_b64rev(src[0]), b = mg_b64rev(src[1]), c = mg_b64rev(src[2]),
+ d = mg_b64rev(src[3]);
+ if (a == 64 || a < 0 || b == 64 || b < 0 || c < 0 || d < 0) return 0;
+ dst[len++] = (char) ((a << 2) | (b >> 4));
+ if (src[2] != '=') {
+ dst[len++] = (char) ((b << 4) | (c >> 2));
+ if (src[3] != '=') dst[len++] = (char) ((c << 6) | d);
+ }
+ src += 4;
+ }
+ dst[len] = '\0';
+ return len;
+}
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/dns.c"
+#endif
+
+
+
+
+
+
+
+
+struct dns_data {
+ struct dns_data *next;
+ struct mg_connection *c;
+ uint64_t expire;
+ uint16_t txnid;
+};
+
+static void mg_sendnsreq(struct mg_connection *, struct mg_str *, int,
+ struct mg_dns *, bool);
+
+static void mg_dns_free(struct mg_connection *c, struct dns_data *d) {
+ LIST_DELETE(struct dns_data,
+ (struct dns_data **) &c->mgr->active_dns_requests, d);
+ free(d);
+}
+
+void mg_resolve_cancel(struct mg_connection *c) {
+ struct dns_data *tmp, *d = (struct dns_data *) c->mgr->active_dns_requests;
+ for (; d != NULL; d = tmp) {
+ tmp = d->next;
+ if (d->c == c) mg_dns_free(c, d);
+ }
+}
+
+static size_t mg_dns_parse_name_depth(const uint8_t *s, size_t len, size_t ofs,
+ char *to, size_t tolen, size_t j,
+ int depth) {
+ size_t i = 0;
+ if (tolen > 0 && depth == 0) to[0] = '\0';
+ if (depth > 5) return 0;
+ // MG_INFO(("ofs %lx %x %x", (unsigned long) ofs, s[ofs], s[ofs + 1]));
+ while (ofs + i + 1 < len) {
+ size_t n = s[ofs + i];
+ if (n == 0) {
+ i++;
+ break;
+ }
+ if (n & 0xc0) {
+ size_t ptr = (((n & 0x3f) << 8) | s[ofs + i + 1]); // 12 is hdr len
+ // MG_INFO(("PTR %lx", (unsigned long) ptr));
+ if (ptr + 1 < len && (s[ptr] & 0xc0) == 0 &&
+ mg_dns_parse_name_depth(s, len, ptr, to, tolen, j, depth + 1) == 0)
+ return 0;
+ i += 2;
+ break;
+ }
+ if (ofs + i + n + 1 >= len) return 0;
+ if (j > 0) {
+ if (j < tolen) to[j] = '.';
+ j++;
+ }
+ if (j + n < tolen) memcpy(&to[j], &s[ofs + i + 1], n);
+ j += n;
+ i += n + 1;
+ if (j < tolen) to[j] = '\0'; // Zero-terminate this chunk
+ // MG_INFO(("--> [%s]", to));
+ }
+ if (tolen > 0) to[tolen - 1] = '\0'; // Make sure make sure it is nul-term
+ return i;
+}
+
+static size_t mg_dns_parse_name(const uint8_t *s, size_t n, size_t ofs,
+ char *dst, size_t dstlen) {
+ return mg_dns_parse_name_depth(s, n, ofs, dst, dstlen, 0, 0);
+}
+
+size_t mg_dns_parse_rr(const uint8_t *buf, size_t len, size_t ofs,
+ bool is_question, struct mg_dns_rr *rr) {
+ const uint8_t *s = buf + ofs, *e = &buf[len];
+
+ memset(rr, 0, sizeof(*rr));
+ if (len < sizeof(struct mg_dns_header)) return 0; // Too small
+ if (len > 512) return 0; // Too large, we don't expect that
+ if (s >= e) return 0; // Overflow
+
+ if ((rr->nlen = (uint16_t) mg_dns_parse_name(buf, len, ofs, NULL, 0)) == 0)
+ return 0;
+ s += rr->nlen + 4;
+ if (s > e) return 0;
+ rr->atype = (uint16_t) (((uint16_t) s[-4] << 8) | s[-3]);
+ rr->aclass = (uint16_t) (((uint16_t) s[-2] << 8) | s[-1]);
+ if (is_question) return (size_t) (rr->nlen + 4);
+
+ s += 6;
+ if (s > e) return 0;
+ rr->alen = (uint16_t) (((uint16_t) s[-2] << 8) | s[-1]);
+ if (s + rr->alen > e) return 0;
+ return (size_t) (rr->nlen + rr->alen + 10);
+}
+
+bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *dm) {
+ const struct mg_dns_header *h = (struct mg_dns_header *) buf;
+ struct mg_dns_rr rr;
+ size_t i, n, ofs = sizeof(*h);
+ memset(dm, 0, sizeof(*dm));
+
+ if (len < sizeof(*h)) return 0; // Too small, headers dont fit
+ if (mg_ntohs(h->num_questions) > 1) return 0; // Sanity
+ if (mg_ntohs(h->num_answers) > 10) return 0; // Sanity
+ dm->txnid = mg_ntohs(h->txnid);
+
+ for (i = 0; i < mg_ntohs(h->num_questions); i++) {
+ if ((n = mg_dns_parse_rr(buf, len, ofs, true, &rr)) == 0) return false;
+ // MG_INFO(("Q %lu %lu %hu/%hu", ofs, n, rr.atype, rr.aclass));
+ ofs += n;
+ }
+ for (i = 0; i < mg_ntohs(h->num_answers); i++) {
+ if ((n = mg_dns_parse_rr(buf, len, ofs, false, &rr)) == 0) return false;
+ // MG_INFO(("A -- %lu %lu %hu/%hu %s", ofs, n, rr.atype, rr.aclass,
+ // dm->name));
+ mg_dns_parse_name(buf, len, ofs, dm->name, sizeof(dm->name));
+ ofs += n;
+
+ if (rr.alen == 4 && rr.atype == 1 && rr.aclass == 1) {
+ dm->addr.is_ip6 = false;
+ memcpy(&dm->addr.ip, &buf[ofs - 4], 4);
+ dm->resolved = true;
+ break; // Return success
+ } else if (rr.alen == 16 && rr.atype == 28 && rr.aclass == 1) {
+ dm->addr.is_ip6 = true;
+ memcpy(&dm->addr.ip6, &buf[ofs - 16], 16);
+ dm->resolved = true;
+ break; // Return success
+ }
+ }
+ return true;
+}
+
+static void dns_cb(struct mg_connection *c, int ev, void *ev_data,
+ void *fn_data) {
+ struct dns_data *d, *tmp;
+ if (ev == MG_EV_POLL) {
+ uint64_t now = *(uint64_t *) ev_data;
+ for (d = (struct dns_data *) c->mgr->active_dns_requests; d != NULL;
+ d = tmp) {
+ tmp = d->next;
+ // MG_DEBUG ("%lu %lu dns poll", d->expire, now));
+ if (now > d->expire) mg_error(d->c, "DNS timeout");
+ }
+ } else if (ev == MG_EV_READ) {
+ struct mg_dns_message dm;
+ int resolved = 0;
+ if (mg_dns_parse(c->recv.buf, c->recv.len, &dm) == false) {
+ MG_ERROR(("Unexpected DNS response:"));
+ mg_hexdump(c->recv.buf, c->recv.len);
+ } else {
+ // MG_VERBOSE(("%s %d", dm.name, dm.resolved));
+ for (d = (struct dns_data *) c->mgr->active_dns_requests; d != NULL;
+ d = tmp) {
+ tmp = d->next;
+ // MG_INFO(("d %p %hu %hu", d, d->txnid, dm.txnid));
+ if (dm.txnid != d->txnid) continue;
+ if (d->c->is_resolving) {
+ if (dm.resolved) {
+ dm.addr.port = d->c->rem.port; // Save port
+ d->c->rem = dm.addr; // Copy resolved address
+ MG_DEBUG(
+ ("%lu %s is %M", d->c->id, dm.name, mg_print_ip, &d->c->rem));
+ mg_connect_resolved(d->c);
+#if MG_ENABLE_IPV6
+ } else if (dm.addr.is_ip6 == false && dm.name[0] != '\0' &&
+ c->mgr->use_dns6 == false) {
+ struct mg_str x = mg_str(dm.name);
+ mg_sendnsreq(d->c, &x, c->mgr->dnstimeout, &c->mgr->dns6, true);
+#endif
+ } else {
+ mg_error(d->c, "%s DNS lookup failed", dm.name);
+ }
+ } else {
+ MG_ERROR(("%lu already resolved", d->c->id));
+ }
+ mg_dns_free(c, d);
+ resolved = 1;
+ }
+ }
+ if (!resolved) MG_ERROR(("stray DNS reply"));
+ c->recv.len = 0;
+ } else if (ev == MG_EV_CLOSE) {
+ for (d = (struct dns_data *) c->mgr->active_dns_requests; d != NULL;
+ d = tmp) {
+ tmp = d->next;
+ mg_error(d->c, "DNS error");
+ mg_dns_free(c, d);
+ }
+ }
+ (void) fn_data;
+}
+
+static bool mg_dns_send(struct mg_connection *c, const struct mg_str *name,
+ uint16_t txnid, bool ipv6) {
+ struct {
+ struct mg_dns_header header;
+ uint8_t data[256];
+ } pkt;
+ size_t i, n;
+ memset(&pkt, 0, sizeof(pkt));
+ pkt.header.txnid = mg_htons(txnid);
+ pkt.header.flags = mg_htons(0x100);
+ pkt.header.num_questions = mg_htons(1);
+ for (i = n = 0; i < sizeof(pkt.data) - 5; i++) {
+ if (name->ptr[i] == '.' || i >= name->len) {
+ pkt.data[n] = (uint8_t) (i - n);
+ memcpy(&pkt.data[n + 1], name->ptr + n, i - n);
+ n = i + 1;
+ }
+ if (i >= name->len) break;
+ }
+ memcpy(&pkt.data[n], "\x00\x00\x01\x00\x01", 5); // A query
+ n += 5;
+ if (ipv6) pkt.data[n - 3] = 0x1c; // AAAA query
+ // memcpy(&pkt.data[n], "\xc0\x0c\x00\x1c\x00\x01", 6); // AAAA query
+ // n += 6;
+ return mg_send(c, &pkt, sizeof(pkt.header) + n);
+}
+
+static void mg_sendnsreq(struct mg_connection *c, struct mg_str *name, int ms,
+ struct mg_dns *dnsc, bool ipv6) {
+ struct dns_data *d = NULL;
+ if (dnsc->url == NULL) {
+ mg_error(c, "DNS server URL is NULL. Call mg_mgr_init()");
+ } else if (dnsc->c == NULL) {
+ dnsc->c = mg_connect(c->mgr, dnsc->url, NULL, NULL);
+ if (dnsc->c != NULL) {
+ dnsc->c->pfn = dns_cb;
+ // dnsc->c->is_hexdumping = 1;
+ }
+ }
+ if (dnsc->c == NULL) {
+ mg_error(c, "resolver");
+ } else if ((d = (struct dns_data *) calloc(1, sizeof(*d))) == NULL) {
+ mg_error(c, "resolve OOM");
+ } else {
+ struct dns_data *reqs = (struct dns_data *) c->mgr->active_dns_requests;
+ d->txnid = reqs ? (uint16_t) (reqs->txnid + 1) : 1;
+ d->next = (struct dns_data *) c->mgr->active_dns_requests;
+ c->mgr->active_dns_requests = d;
+ d->expire = mg_millis() + (uint64_t) ms;
+ d->c = c;
+ c->is_resolving = 1;
+ MG_VERBOSE(("%lu resolving %.*s @ %s, txnid %hu", c->id, (int) name->len,
+ name->ptr, dnsc->url, d->txnid));
+ if (!mg_dns_send(dnsc->c, name, d->txnid, ipv6)) {
+ mg_error(dnsc->c, "DNS send");
+ }
+ }
+}
+
+void mg_resolve(struct mg_connection *c, const char *url) {
+ struct mg_str host = mg_url_host(url);
+ c->rem.port = mg_htons(mg_url_port(url));
+ if (mg_aton(host, &c->rem)) {
+ // host is an IP address, do not fire name resolution
+ mg_connect_resolved(c);
+ } else {
+ // host is not an IP, send DNS resolution request
+ struct mg_dns *dns = c->mgr->use_dns6 ? &c->mgr->dns6 : &c->mgr->dns4;
+ mg_sendnsreq(c, &host, c->mgr->dnstimeout, dns, c->mgr->use_dns6);
+ }
+}
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/event.c"
+#endif
+
+
+
+
+
+void mg_call(struct mg_connection *c, int ev, void *ev_data) {
+ // Run user-defined handler first, in order to give it an ability
+ // to intercept processing (e.g. clean input buffer) before the
+ // protocol handler kicks in
+ if (c->fn != NULL) c->fn(c, ev, ev_data, c->fn_data);
+ if (c->pfn != NULL) c->pfn(c, ev, ev_data, c->pfn_data);
+}
+
+void mg_error(struct mg_connection *c, const char *fmt, ...) {
+ char buf[64];
+ va_list ap;
+ va_start(ap, fmt);
+ mg_vsnprintf(buf, sizeof(buf), fmt, &ap);
+ va_end(ap);
+ MG_ERROR(("%lu %p %s", c->id, c->fd, buf));
+ c->is_closing = 1; // Set is_closing before sending MG_EV_CALL
+ mg_call(c, MG_EV_ERROR, buf); // Let user handler to override it
+}
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/fmt.c"
+#endif
+
+
+
+
+static bool is_digit(int c) {
+ return c >= '0' && c <= '9';
+}
+
+static int addexp(char *buf, int e, int sign) {
+ int n = 0;
+ buf[n++] = 'e';
+ buf[n++] = (char) sign;
+ if (e > 400) return 0;
+ if (e < 10) buf[n++] = '0';
+ if (e >= 100) buf[n++] = (char) (e / 100 + '0'), e -= 100 * (e / 100);
+ if (e >= 10) buf[n++] = (char) (e / 10 + '0'), e -= 10 * (e / 10);
+ buf[n++] = (char) (e + '0');
+ return n;
+}
+
+static int xisinf(double x) {
+ union {
+ double f;
+ uint64_t u;
+ } ieee754 = {x};
+ return ((unsigned) (ieee754.u >> 32) & 0x7fffffff) == 0x7ff00000 &&
+ ((unsigned) ieee754.u == 0);
+}
+
+static int xisnan(double x) {
+ union {
+ double f;
+ uint64_t u;
+ } ieee754 = {x};
+ return ((unsigned) (ieee754.u >> 32) & 0x7fffffff) +
+ ((unsigned) ieee754.u != 0) >
+ 0x7ff00000;
+}
+
+static size_t mg_dtoa(char *dst, size_t dstlen, double d, int width, bool tz) {
+ char buf[40];
+ int i, s = 0, n = 0, e = 0;
+ double t, mul, saved;
+ if (d == 0.0) return mg_snprintf(dst, dstlen, "%s", "0");
+ if (xisinf(d)) return mg_snprintf(dst, dstlen, "%s", d > 0 ? "inf" : "-inf");
+ if (xisnan(d)) return mg_snprintf(dst, dstlen, "%s", "nan");
+ if (d < 0.0) d = -d, buf[s++] = '-';
+
+ // Round
+ saved = d;
+ mul = 1.0;
+ while (d >= 10.0 && d / mul >= 10.0) mul *= 10.0;
+ while (d <= 1.0 && d / mul <= 1.0) mul /= 10.0;
+ for (i = 0, t = mul * 5; i < width; i++) t /= 10.0;
+ d += t;
+ // Calculate exponent, and 'mul' for scientific representation
+ mul = 1.0;
+ while (d >= 10.0 && d / mul >= 10.0) mul *= 10.0, e++;
+ while (d < 1.0 && d / mul < 1.0) mul /= 10.0, e--;
+ // printf(" --> %g %d %g %g\n", saved, e, t, mul);
+
+ if (e >= width && width > 1) {
+ n = (int) mg_dtoa(buf, sizeof(buf), saved / mul, width, tz);
+ // printf(" --> %.*g %d [%.*s]\n", 10, d / t, e, n, buf);
+ n += addexp(buf + s + n, e, '+');
+ return mg_snprintf(dst, dstlen, "%.*s", n, buf);
+ } else if (e <= -width && width > 1) {
+ n = (int) mg_dtoa(buf, sizeof(buf), saved / mul, width, tz);
+ // printf(" --> %.*g %d [%.*s]\n", 10, d / mul, e, n, buf);
+ n += addexp(buf + s + n, -e, '-');
+ return mg_snprintf(dst, dstlen, "%.*s", n, buf);
+ } else {
+ for (i = 0, t = mul; t >= 1.0 && s + n < (int) sizeof(buf); i++) {
+ int ch = (int) (d / t);
+ if (n > 0 || ch > 0) buf[s + n++] = (char) (ch + '0');
+ d -= ch * t;
+ t /= 10.0;
+ }
+ // printf(" --> [%g] -> %g %g (%d) [%.*s]\n", saved, d, t, n, s + n, buf);
+ if (n == 0) buf[s++] = '0';
+ while (t >= 1.0 && n + s < (int) sizeof(buf)) buf[n++] = '0', t /= 10.0;
+ if (s + n < (int) sizeof(buf)) buf[n + s++] = '.';
+ // printf(" 1--> [%g] -> [%.*s]\n", saved, s + n, buf);
+ for (i = 0, t = 0.1; s + n < (int) sizeof(buf) && n < width; i++) {
+ int ch = (int) (d / t);
+ buf[s + n++] = (char) (ch + '0');
+ d -= ch * t;
+ t /= 10.0;
+ }
+ }
+ while (tz && n > 0 && buf[s + n - 1] == '0') n--; // Trim trailing zeroes
+ if (n > 0 && buf[s + n - 1] == '.') n--; // Trim trailing dot
+ n += s;
+ if (n >= (int) sizeof(buf)) n = (int) sizeof(buf) - 1;
+ buf[n] = '\0';
+ return mg_snprintf(dst, dstlen, "%s", buf);
+}
+
+static size_t mg_lld(char *buf, int64_t val, bool is_signed, bool is_hex) {
+ const char *letters = "0123456789abcdef";
+ uint64_t v = (uint64_t) val;
+ size_t s = 0, n, i;
+ if (is_signed && val < 0) buf[s++] = '-', v = (uint64_t) (-val);
+ // This loop prints a number in reverse order. I guess this is because we
+ // write numbers from right to left: least significant digit comes last.
+ // Maybe because we use Arabic numbers, and Arabs write RTL?
+ if (is_hex) {
+ for (n = 0; v; v >>= 4) buf[s + n++] = letters[v & 15];
+ } else {
+ for (n = 0; v; v /= 10) buf[s + n++] = letters[v % 10];
+ }
+ // Reverse a string
+ for (i = 0; i < n / 2; i++) {
+ char t = buf[s + i];
+ buf[s + i] = buf[s + n - i - 1], buf[s + n - i - 1] = t;
+ }
+ if (val == 0) buf[n++] = '0'; // Handle special case
+ return n + s;
+}
+
+static size_t scpy(void (*out)(char, void *), void *ptr, char *buf,
+ size_t len) {
+ size_t i = 0;
+ while (i < len && buf[i] != '\0') out(buf[i++], ptr);
+ return i;
+}
+
+size_t mg_xprintf(void (*out)(char, void *), void *ptr, const char *fmt, ...) {
+ size_t len = 0;
+ va_list ap;
+ va_start(ap, fmt);
+ len = mg_vxprintf(out, ptr, fmt, &ap);
+ va_end(ap);
+ return len;
+}
+
+size_t mg_vxprintf(void (*out)(char, void *), void *param, const char *fmt,
+ va_list *ap) {
+ size_t i = 0, n = 0;
+ while (fmt[i] != '\0') {
+ if (fmt[i] == '%') {
+ size_t j, k, x = 0, is_long = 0, w = 0 /* width */, pr = ~0U /* prec */;
+ char pad = ' ', minus = 0, c = fmt[++i];
+ if (c == '#') x++, c = fmt[++i];
+ if (c == '-') minus++, c = fmt[++i];
+ if (c == '0') pad = '0', c = fmt[++i];
+ while (is_digit(c)) w *= 10, w += (size_t) (c - '0'), c = fmt[++i];
+ if (c == '.') {
+ c = fmt[++i];
+ if (c == '*') {
+ pr = (size_t) va_arg(*ap, int);
+ c = fmt[++i];
+ } else {
+ pr = 0;
+ while (is_digit(c)) pr *= 10, pr += (size_t) (c - '0'), c = fmt[++i];
+ }
+ }
+ while (c == 'h') c = fmt[++i]; // Treat h and hh as int
+ if (c == 'l') {
+ is_long++, c = fmt[++i];
+ if (c == 'l') is_long++, c = fmt[++i];
+ }
+ if (c == 'p') x = 1, is_long = 1;
+ if (c == 'd' || c == 'u' || c == 'x' || c == 'X' || c == 'p' ||
+ c == 'g' || c == 'f') {
+ bool s = (c == 'd'), h = (c == 'x' || c == 'X' || c == 'p');
+ char tmp[40];
+ size_t xl = x ? 2 : 0;
+ if (c == 'g' || c == 'f') {
+ double v = va_arg(*ap, double);
+ if (pr == ~0U) pr = 6;
+ k = mg_dtoa(tmp, sizeof(tmp), v, (int) pr, c == 'g');
+ } else if (is_long == 2) {
+ int64_t v = va_arg(*ap, int64_t);
+ k = mg_lld(tmp, v, s, h);
+ } else if (is_long == 1) {
+ long v = va_arg(*ap, long);
+ k = mg_lld(tmp, s ? (int64_t) v : (int64_t) (unsigned long) v, s, h);
+ } else {
+ int v = va_arg(*ap, int);
+ k = mg_lld(tmp, s ? (int64_t) v : (int64_t) (unsigned) v, s, h);
+ }
+ for (j = 0; j < xl && w > 0; j++) w--;
+ for (j = 0; pad == ' ' && !minus && k < w && j + k < w; j++)
+ n += scpy(out, param, &pad, 1);
+ n += scpy(out, param, (char *) "0x", xl);
+ for (j = 0; pad == '0' && k < w && j + k < w; j++)
+ n += scpy(out, param, &pad, 1);
+ n += scpy(out, param, tmp, k);
+ for (j = 0; pad == ' ' && minus && k < w && j + k < w; j++)
+ n += scpy(out, param, &pad, 1);
+ } else if (c == 'm' || c == 'M') {
+ mg_pm_t f = va_arg(*ap, mg_pm_t);
+ if (c == 'm') out('"', param);
+ n += f(out, param, ap);
+ if (c == 'm') n += 2, out('"', param);
+ } else if (c == 'c') {
+ int ch = va_arg(*ap, int);
+ out((char) ch, param);
+ n++;
+ } else if (c == 's') {
+ char *p = va_arg(*ap, char *);
+ if (pr == ~0U) pr = p == NULL ? 0 : strlen(p);
+ for (j = 0; !minus && pr < w && j + pr < w; j++)
+ n += scpy(out, param, &pad, 1);
+ n += scpy(out, param, p, pr);
+ for (j = 0; minus && pr < w && j + pr < w; j++)
+ n += scpy(out, param, &pad, 1);
+ } else if (c == '%') {
+ out('%', param);
+ n++;
+ } else {
+ out('%', param);
+ out(c, param);
+ n += 2;
+ }
+ i++;
+ } else {
+ out(fmt[i], param), n++, i++;
+ }
+ }
+ return n;
+}
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/fs.c"
+#endif
+
+
+
+struct mg_fd *mg_fs_open(struct mg_fs *fs, const char *path, int flags) {
+ struct mg_fd *fd = (struct mg_fd *) calloc(1, sizeof(*fd));
+ if (fd != NULL) {
+ fd->fd = fs->op(path, flags);
+ fd->fs = fs;
+ if (fd->fd == NULL) {
+ free(fd);
+ fd = NULL;
+ }
+ }
+ return fd;
+}
+
+void mg_fs_close(struct mg_fd *fd) {
+ if (fd != NULL) {
+ fd->fs->cl(fd->fd);
+ free(fd);
+ }
+}
+
+char *mg_file_read(struct mg_fs *fs, const char *path, size_t *sizep) {
+ struct mg_fd *fd;
+ char *data = NULL;
+ size_t size = 0;
+ fs->st(path, &size, NULL);
+ if ((fd = mg_fs_open(fs, path, MG_FS_READ)) != NULL) {
+ data = (char *) calloc(1, size + 1);
+ if (data != NULL) {
+ if (fs->rd(fd->fd, data, size) != size) {
+ free(data);
+ data = NULL;
+ } else {
+ data[size] = '\0';
+ if (sizep != NULL) *sizep = size;
+ }
+ }
+ mg_fs_close(fd);
+ }
+ return data;
+}
+
+bool mg_file_write(struct mg_fs *fs, const char *path, const void *buf,
+ size_t len) {
+ bool result = false;
+ struct mg_fd *fd;
+ char tmp[MG_PATH_MAX];
+ mg_snprintf(tmp, sizeof(tmp), "%s..%d", path, rand());
+ if ((fd = mg_fs_open(fs, tmp, MG_FS_WRITE)) != NULL) {
+ result = fs->wr(fd->fd, buf, len) == len;
+ mg_fs_close(fd);
+ if (result) {
+ fs->rm(path);
+ fs->mv(tmp, path);
+ } else {
+ fs->rm(tmp);
+ }
+ }
+ return result;
+}
+
+bool mg_file_printf(struct mg_fs *fs, const char *path, const char *fmt, ...) {
+ va_list ap;
+ char *data;
+ bool result = false;
+ va_start(ap, fmt);
+ data = mg_vmprintf(fmt, &ap);
+ va_end(ap);
+ result = mg_file_write(fs, path, data, strlen(data));
+ free(data);
+ return result;
+}
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/fs_fat.c"
+#endif
+
+
+
+#if MG_ENABLE_FATFS
+#include <ff.h>
+
+static int mg_days_from_epoch(int y, int m, int d) {
+ y -= m <= 2;
+ int era = y / 400;
+ int yoe = y - era * 400;
+ int doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1;
+ int doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
+ return era * 146097 + doe - 719468;
+}
+
+static time_t mg_timegm(const struct tm *t) {
+ int year = t->tm_year + 1900;
+ int month = t->tm_mon; // 0-11
+ if (month > 11) {
+ year += month / 12;
+ month %= 12;
+ } else if (month < 0) {
+ int years_diff = (11 - month) / 12;
+ year -= years_diff;
+ month += 12 * years_diff;
+ }
+ int x = mg_days_from_epoch(year, month + 1, t->tm_mday);
+ return 60 * (60 * (24L * x + t->tm_hour) + t->tm_min) + t->tm_sec;
+}
+
+static time_t ff_time_to_epoch(uint16_t fdate, uint16_t ftime) {
+ struct tm tm;
+ memset(&tm, 0, sizeof(struct tm));
+ tm.tm_sec = (ftime << 1) & 0x3e;
+ tm.tm_min = ((ftime >> 5) & 0x3f);
+ tm.tm_hour = ((ftime >> 11) & 0x1f);
+ tm.tm_mday = (fdate & 0x1f);
+ tm.tm_mon = ((fdate >> 5) & 0x0f) - 1;
+ tm.tm_year = ((fdate >> 9) & 0x7f) + 80;
+ return mg_timegm(&tm);
+}
+
+static int ff_stat(const char *path, size_t *size, time_t *mtime) {
+ FILINFO fi;
+ if (path[0] == '\0') {
+ if (size) *size = 0;
+ if (mtime) *mtime = 0;
+ return MG_FS_DIR;
+ } else if (f_stat(path, &fi) == 0) {
+ if (size) *size = (size_t) fi.fsize;
+ if (mtime) *mtime = ff_time_to_epoch(fi.fdate, fi.ftime);
+ return MG_FS_READ | MG_FS_WRITE | ((fi.fattrib & AM_DIR) ? MG_FS_DIR : 0);
+ } else {
+ return 0;
+ }
+}
+
+static void ff_list(const char *dir, void (*fn)(const char *, void *),
+ void *userdata) {
+ DIR d;
+ FILINFO fi;
+ if (f_opendir(&d, dir) == FR_OK) {
+ while (f_readdir(&d, &fi) == FR_OK && fi.fname[0] != '\0') {
+ if (!strcmp(fi.fname, ".") || !strcmp(fi.fname, "..")) continue;
+ fn(fi.fname, userdata);
+ }
+ f_closedir(&d);
+ }
+}
+
+static void *ff_open(const char *path, int flags) {
+ FIL f;
+ unsigned char mode = FA_READ;
+ if (flags & MG_FS_WRITE) mode |= FA_WRITE | FA_OPEN_ALWAYS | FA_OPEN_APPEND;
+ if (f_open(&f, path, mode) == 0) {
+ FIL *fp;
+ if ((fp = calloc(1, sizeof(*fp))) != NULL) {
+ memcpy(fp, &f, sizeof(*fp));
+ return fp;
+ }
+ }
+ return NULL;
+}
+
+static void ff_close(void *fp) {
+ if (fp != NULL) {
+ f_close((FIL *) fp);
+ free(fp);
+ }
+}
+
+static size_t ff_read(void *fp, void *buf, size_t len) {
+ UINT n = 0, misalign = ((size_t) buf) & 3;
+ if (misalign) {
+ char aligned[4];
+ f_read((FIL *) fp, aligned, len > misalign ? misalign : len, &n);
+ memcpy(buf, aligned, n);
+ } else {
+ f_read((FIL *) fp, buf, len, &n);
+ }
+ return n;
+}
+
+static size_t ff_write(void *fp, const void *buf, size_t len) {
+ UINT n = 0;
+ return f_write((FIL *) fp, (char *) buf, len, &n) == FR_OK ? n : 0;
+}
+
+static size_t ff_seek(void *fp, size_t offset) {
+ f_lseek((FIL *) fp, offset);
+ return offset;
+}
+
+static bool ff_rename(const char *from, const char *to) {
+ return f_rename(from, to) == FR_OK;
+}
+
+static bool ff_remove(const char *path) {
+ return f_unlink(path) == FR_OK;
+}
+
+static bool ff_mkdir(const char *path) {
+ return f_mkdir(path) == FR_OK;
+}
+
+struct mg_fs mg_fs_fat = {ff_stat, ff_list, ff_open, ff_close, ff_read,
+ ff_write, ff_seek, ff_rename, ff_remove, ff_mkdir};
+#endif
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/fs_packed.c"
+#endif
+
+
+
+
+struct packed_file {
+ const char *data;
+ size_t size;
+ size_t pos;
+};
+
+const char *mg_unpack(const char *path, size_t *size, time_t *mtime);
+const char *mg_unlist(size_t no);
+
+#if MG_ENABLE_PACKED_FS
+#else
+const char *mg_unpack(const char *path, size_t *size, time_t *mtime) {
+ (void) path, (void) size, (void) mtime;
+ return NULL;
+}
+const char *mg_unlist(size_t no) {
+ (void) no;
+ return NULL;
+}
+#endif
+
+static int is_dir_prefix(const char *prefix, size_t n, const char *path) {
+ // MG_INFO(("[%.*s] [%s] %c", (int) n, prefix, path, path[n]));
+ return n < strlen(path) && strncmp(prefix, path, n) == 0 &&
+ (n == 0 || path[n] == '/' || path[n - 1] == '/');
+}
+
+static int packed_stat(const char *path, size_t *size, time_t *mtime) {
+ const char *p;
+ size_t i, n = strlen(path);
+ if (mg_unpack(path, size, mtime)) return MG_FS_READ; // Regular file
+ // Scan all files. If `path` is a dir prefix for any of them, it's a dir
+ for (i = 0; (p = mg_unlist(i)) != NULL; i++) {
+ if (is_dir_prefix(path, n, p)) return MG_FS_DIR;
+ }
+ return 0;
+}
+
+static void packed_list(const char *dir, void (*fn)(const char *, void *),
+ void *userdata) {
+ char buf[MG_PATH_MAX], tmp[sizeof(buf)];
+ const char *path, *begin, *end;
+ size_t i, n = strlen(dir);
+ tmp[0] = '\0'; // Previously listed entry
+ for (i = 0; (path = mg_unlist(i)) != NULL; i++) {
+ if (!is_dir_prefix(dir, n, path)) continue;
+ begin = &path[n + 1];
+ end = strchr(begin, '/');
+ if (end == NULL) end = begin + strlen(begin);
+ mg_snprintf(buf, sizeof(buf), "%.*s", (int) (end - begin), begin);
+ buf[sizeof(buf) - 1] = '\0';
+ // If this entry has been already listed, skip
+ // NOTE: we're assuming that file list is sorted alphabetically
+ if (strcmp(buf, tmp) == 0) continue;
+ fn(buf, userdata); // Not yet listed, call user function
+ strcpy(tmp, buf); // And save this entry as listed
+ }
+}
+
+static void *packed_open(const char *path, int flags) {
+ size_t size = 0;
+ const char *data = mg_unpack(path, &size, NULL);
+ struct packed_file *fp = NULL;
+ if (data == NULL) return NULL;
+ if (flags & MG_FS_WRITE) return NULL;
+ if ((fp = (struct packed_file *) calloc(1, sizeof(*fp))) != NULL) {
+ fp->size = size;
+ fp->data = data;
+ }
+ return (void *) fp;
+}
+
+static void packed_close(void *fp) {
+ if (fp != NULL) free(fp);
+}
+
+static size_t packed_read(void *fd, void *buf, size_t len) {
+ struct packed_file *fp = (struct packed_file *) fd;
+ if (fp->pos + len > fp->size) len = fp->size - fp->pos;
+ memcpy(buf, &fp->data[fp->pos], len);
+ fp->pos += len;
+ return len;
+}
+
+static size_t packed_write(void *fd, const void *buf, size_t len) {
+ (void) fd, (void) buf, (void) len;
+ return 0;
+}
+
+static size_t packed_seek(void *fd, size_t offset) {
+ struct packed_file *fp = (struct packed_file *) fd;
+ fp->pos = offset;
+ if (fp->pos > fp->size) fp->pos = fp->size;
+ return fp->pos;
+}
+
+static bool packed_rename(const char *from, const char *to) {
+ (void) from, (void) to;
+ return false;
+}
+
+static bool packed_remove(const char *path) {
+ (void) path;
+ return false;
+}
+
+static bool packed_mkdir(const char *path) {
+ (void) path;
+ return false;
+}
+
+struct mg_fs mg_fs_packed = {
+ packed_stat, packed_list, packed_open, packed_close, packed_read,
+ packed_write, packed_seek, packed_rename, packed_remove, packed_mkdir};
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/fs_posix.c"
+#endif
+
+
+#if MG_ENABLE_FILE
+
+#ifndef MG_STAT_STRUCT
+#define MG_STAT_STRUCT stat
+#endif
+
+#ifndef MG_STAT_FUNC
+#define MG_STAT_FUNC stat
+#endif
+
+static int p_stat(const char *path, size_t *size, time_t *mtime) {
+#if !defined(S_ISDIR)
+ MG_ERROR(("stat() API is not supported. %p %p %p", path, size, mtime));
+ return 0;
+#else
+#if MG_ARCH == MG_ARCH_WIN32
+ struct _stati64 st;
+ wchar_t tmp[MG_PATH_MAX];
+ MultiByteToWideChar(CP_UTF8, 0, path, -1, tmp, sizeof(tmp) / sizeof(tmp[0]));
+ if (_wstati64(tmp, &st) != 0) return 0;
+ // If path is a symlink, windows reports 0 in st.st_size.
+ // Get a real file size by opening it and jumping to the end
+ if (st.st_size == 0 && (st.st_mode & _S_IFREG)) {
+ FILE *fp = _wfopen(tmp, L"rb");
+ if (fp != NULL) {
+ fseek(fp, 0, SEEK_END);
+ if (ftell(fp) > 0) st.st_size = ftell(fp); // Use _ftelli64 on win10+
+ fclose(fp);
+ }
+ }
+#else
+ struct MG_STAT_STRUCT st;
+ if (MG_STAT_FUNC(path, &st) != 0) return 0;
+#endif
+ if (size) *size = (size_t) st.st_size;
+ if (mtime) *mtime = st.st_mtime;
+ return MG_FS_READ | MG_FS_WRITE | (S_ISDIR(st.st_mode) ? MG_FS_DIR : 0);
+#endif
+}
+
+#if MG_ARCH == MG_ARCH_WIN32
+struct dirent {
+ char d_name[MAX_PATH];
+};
+
+typedef struct win32_dir {
+ HANDLE handle;
+ WIN32_FIND_DATAW info;
+ struct dirent result;
+} DIR;
+
+int gettimeofday(struct timeval *tv, void *tz) {
+ FILETIME ft;
+ unsigned __int64 tmpres = 0;
+
+ if (tv != NULL) {
+ GetSystemTimeAsFileTime(&ft);
+ tmpres |= ft.dwHighDateTime;
+ tmpres <<= 32;
+ tmpres |= ft.dwLowDateTime;
+ tmpres /= 10; // convert into microseconds
+ tmpres -= (int64_t) 11644473600000000;
+ tv->tv_sec = (long) (tmpres / 1000000UL);
+ tv->tv_usec = (long) (tmpres % 1000000UL);
+ }
+ (void) tz;
+ return 0;
+}
+
+static int to_wchar(const char *path, wchar_t *wbuf, size_t wbuf_len) {
+ int ret;
+ char buf[MAX_PATH * 2], buf2[MAX_PATH * 2], *p;
+ strncpy(buf, path, sizeof(buf));
+ buf[sizeof(buf) - 1] = '\0';
+ // Trim trailing slashes. Leave backslash for paths like "X:\"
+ p = buf + strlen(buf) - 1;
+ while (p > buf && p[-1] != ':' && (p[0] == '\\' || p[0] == '/')) *p-- = '\0';
+ memset(wbuf, 0, wbuf_len * sizeof(wchar_t));
+ ret = MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len);
+ // Convert back to Unicode. If doubly-converted string does not match the
+ // original, something is fishy, reject.
+ WideCharToMultiByte(CP_UTF8, 0, wbuf, (int) wbuf_len, buf2, sizeof(buf2),
+ NULL, NULL);
+ if (strcmp(buf, buf2) != 0) {
+ wbuf[0] = L'\0';
+ ret = 0;
+ }
+ return ret;
+}
+
+DIR *opendir(const char *name) {
+ DIR *d = NULL;
+ wchar_t wpath[MAX_PATH];
+ DWORD attrs;
+
+ if (name == NULL) {
+ SetLastError(ERROR_BAD_ARGUMENTS);
+ } else if ((d = (DIR *) calloc(1, sizeof(*d))) == NULL) {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ } else {
+ to_wchar(name, wpath, sizeof(wpath) / sizeof(wpath[0]));
+ attrs = GetFileAttributesW(wpath);
+ if (attrs != 0Xffffffff && (attrs & FILE_ATTRIBUTE_DIRECTORY)) {
+ (void) wcscat(wpath, L"\\*");
+ d->handle = FindFirstFileW(wpath, &d->info);
+ d->result.d_name[0] = '\0';
+ } else {
+ free(d);
+ d = NULL;
+ }
+ }
+ return d;
+}
+
+int closedir(DIR *d) {
+ int result = 0;
+ if (d != NULL) {
+ if (d->handle != INVALID_HANDLE_VALUE)
+ result = FindClose(d->handle) ? 0 : -1;
+ free(d);
+ } else {
+ result = -1;
+ SetLastError(ERROR_BAD_ARGUMENTS);
+ }
+ return result;
+}
+
+struct dirent *readdir(DIR *d) {
+ struct dirent *result = NULL;
+ if (d != NULL) {
+ memset(&d->result, 0, sizeof(d->result));
+ if (d->handle != INVALID_HANDLE_VALUE) {
+ result = &d->result;
+ WideCharToMultiByte(CP_UTF8, 0, d->info.cFileName, -1, result->d_name,
+ sizeof(result->d_name), NULL, NULL);
+ if (!FindNextFileW(d->handle, &d->info)) {
+ FindClose(d->handle);
+ d->handle = INVALID_HANDLE_VALUE;
+ }
+ } else {
+ SetLastError(ERROR_FILE_NOT_FOUND);
+ }
+ } else {
+ SetLastError(ERROR_BAD_ARGUMENTS);
+ }
+ return result;
+}
+#endif
+
+static void p_list(const char *dir, void (*fn)(const char *, void *),
+ void *userdata) {
+#if MG_ENABLE_DIRLIST
+ struct dirent *dp;
+ DIR *dirp;
+ if ((dirp = (opendir(dir))) == NULL) return;
+ while ((dp = readdir(dirp)) != NULL) {
+ if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue;
+ fn(dp->d_name, userdata);
+ }
+ closedir(dirp);
+#else
+ (void) dir, (void) fn, (void) userdata;
+#endif
+}
+
+static void *p_open(const char *path, int flags) {
+ const char *mode = flags == MG_FS_READ ? "rb" : "a+b";
+#if MG_ARCH == MG_ARCH_WIN32
+ wchar_t b1[MG_PATH_MAX], b2[10];
+ MultiByteToWideChar(CP_UTF8, 0, path, -1, b1, sizeof(b1) / sizeof(b1[0]));
+ MultiByteToWideChar(CP_UTF8, 0, mode, -1, b2, sizeof(b2) / sizeof(b2[0]));
+ return (void *) _wfopen(b1, b2);
+#else
+ return (void *) fopen(path, mode);
+#endif
+}
+
+static void p_close(void *fp) {
+ fclose((FILE *) fp);
+}
+
+static size_t p_read(void *fp, void *buf, size_t len) {
+ return fread(buf, 1, len, (FILE *) fp);
+}
+
+static size_t p_write(void *fp, const void *buf, size_t len) {
+ return fwrite(buf, 1, len, (FILE *) fp);
+}
+
+static size_t p_seek(void *fp, size_t offset) {
+#if (defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64) || \
+ (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L) || \
+ (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 600)
+ if (fseeko((FILE *) fp, (off_t) offset, SEEK_SET) != 0) (void) 0;
+#else
+ if (fseek((FILE *) fp, (long) offset, SEEK_SET) != 0) (void) 0;
+#endif
+ return (size_t) ftell((FILE *) fp);
+}
+
+static bool p_rename(const char *from, const char *to) {
+ return rename(from, to) == 0;
+}
+
+static bool p_remove(const char *path) {
+ return remove(path) == 0;
+}
+
+static bool p_mkdir(const char *path) {
+ return mkdir(path, 0775) == 0;
+}
+
+#else
+
+static int p_stat(const char *path, size_t *size, time_t *mtime) {
+ (void) path, (void) size, (void) mtime;
+ return 0;
+}
+static void p_list(const char *path, void (*fn)(const char *, void *),
+ void *userdata) {
+ (void) path, (void) fn, (void) userdata;
+}
+static void *p_open(const char *path, int flags) {
+ (void) path, (void) flags;
+ return NULL;
+}
+static void p_close(void *fp) {
+ (void) fp;
+}
+static size_t p_read(void *fd, void *buf, size_t len) {
+ (void) fd, (void) buf, (void) len;
+ return 0;
+}
+static size_t p_write(void *fd, const void *buf, size_t len) {
+ (void) fd, (void) buf, (void) len;
+ return 0;
+}
+static size_t p_seek(void *fd, size_t offset) {
+ (void) fd, (void) offset;
+ return (size_t) ~0;
+}
+static bool p_rename(const char *from, const char *to) {
+ (void) from, (void) to;
+ return false;
+}
+static bool p_remove(const char *path) {
+ (void) path;
+ return false;
+}
+static bool p_mkdir(const char *path) {
+ (void) path;
+ return false;
+}
+#endif
+
+struct mg_fs mg_fs_posix = {p_stat, p_list, p_open, p_close, p_read,
+ p_write, p_seek, p_rename, p_remove, p_mkdir};
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/http.c"
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+bool mg_to_size_t(struct mg_str str, size_t *val);
+bool mg_to_size_t(struct mg_str str, size_t *val) {
+ uint64_t result = 0, max = 1844674407370955160 /* (UINT64_MAX-9)/10 */;
+ size_t i = 0;
+ while (i < str.len && (str.ptr[i] == ' ' || str.ptr[i] == '\t')) i++;
+ if (i < str.len && str.ptr[i] == '-') return false;
+ while (i < str.len && str.ptr[i] >= '0' && str.ptr[i] <= '9') {
+ if (result > max) return false;
+ result *= 10;
+ result += (unsigned) (str.ptr[i] - '0');
+ i++;
+ }
+ *val = (size_t) result;
+ return true;
+}
+
+// Chunk deletion marker is the MSB in the "processed" counter
+#define MG_DMARK ((size_t) 1 << (sizeof(size_t) * 8 - 1))
+
+// Multipart POST example:
+// --xyz
+// Content-Disposition: form-data; name="val"
+//
+// abcdef
+// --xyz
+// Content-Disposition: form-data; name="foo"; filename="a.txt"
+// Content-Type: text/plain
+//
+// hello world
+//
+// --xyz--
+size_t mg_http_next_multipart(struct mg_str body, size_t ofs,
+ struct mg_http_part *part) {
+ struct mg_str cd = mg_str_n("Content-Disposition", 19);
+ const char *s = body.ptr;
+ size_t b = ofs, h1, h2, b1, b2, max = body.len;
+
+ // Init part params
+ if (part != NULL) part->name = part->filename = part->body = mg_str_n(0, 0);
+
+ // Skip boundary
+ while (b + 2 < max && s[b] != '\r' && s[b + 1] != '\n') b++;
+ if (b <= ofs || b + 2 >= max) return 0;
+ // MG_INFO(("B: %zu %zu [%.*s]", ofs, b - ofs, (int) (b - ofs), s));
+
+ // Skip headers
+ h1 = h2 = b + 2;
+ for (;;) {
+ while (h2 + 2 < max && s[h2] != '\r' && s[h2 + 1] != '\n') h2++;
+ if (h2 == h1) break;
+ if (h2 + 2 >= max) return 0;
+ // MG_INFO(("Header: [%.*s]", (int) (h2 - h1), &s[h1]));
+ if (part != NULL && h1 + cd.len + 2 < h2 && s[h1 + cd.len] == ':' &&
+ mg_ncasecmp(&s[h1], cd.ptr, cd.len) == 0) {
+ struct mg_str v = mg_str_n(&s[h1 + cd.len + 2], h2 - (h1 + cd.len + 2));
+ part->name = mg_http_get_header_var(v, mg_str_n("name", 4));
+ part->filename = mg_http_get_header_var(v, mg_str_n("filename", 8));
+ }
+ h1 = h2 = h2 + 2;
+ }
+ b1 = b2 = h2 + 2;
+ while (b2 + 2 + (b - ofs) + 2 < max && !(s[b2] == '\r' && s[b2 + 1] == '\n' &&
+ memcmp(&s[b2 + 2], s, b - ofs) == 0))
+ b2++;
+
+ if (b2 + 2 >= max) return 0;
+ if (part != NULL) part->body = mg_str_n(&s[b1], b2 - b1);
+ // MG_INFO(("Body: [%.*s]", (int) (b2 - b1), &s[b1]));
+ return b2 + 2;
+}
+
+void mg_http_bauth(struct mg_connection *c, const char *user,
+ const char *pass) {
+ struct mg_str u = mg_str(user), p = mg_str(pass);
+ size_t need = c->send.len + 36 + (u.len + p.len) * 2;
+ if (c->send.size < need) mg_iobuf_resize(&c->send, need);
+ if (c->send.size >= need) {
+ int i, n = 0;
+ char *buf = (char *) &c->send.buf[c->send.len];
+ memcpy(buf, "Authorization: Basic ", 21); // DON'T use mg_send!
+ for (i = 0; i < (int) u.len; i++) {
+ n = mg_base64_update(((unsigned char *) u.ptr)[i], buf + 21, n);
+ }
+ if (p.len > 0) {
+ n = mg_base64_update(':', buf + 21, n);
+ for (i = 0; i < (int) p.len; i++) {
+ n = mg_base64_update(((unsigned char *) p.ptr)[i], buf + 21, n);
+ }
+ }
+ n = mg_base64_final(buf + 21, n);
+ c->send.len += 21 + (size_t) n + 2;
+ memcpy(&c->send.buf[c->send.len - 2], "\r\n", 2);
+ } else {
+ MG_ERROR(("%lu oom %d->%d ", c->id, (int) c->send.size, (int) need));
+ }
+}
+
+struct mg_str mg_http_var(struct mg_str buf, struct mg_str name) {
+ struct mg_str k, v, result = mg_str_n(NULL, 0);
+ while (mg_split(&buf, &k, &v, '&')) {
+ if (name.len == k.len && mg_ncasecmp(name.ptr, k.ptr, k.len) == 0) {
+ result = v;
+ break;
+ }
+ }
+ return result;
+}
+
+int mg_http_get_var(const struct mg_str *buf, const char *name, char *dst,
+ size_t dst_len) {
+ int len;
+ if (dst == NULL || dst_len == 0) {
+ len = -2; // Bad destination
+ } else if (buf->ptr == NULL || name == NULL || buf->len == 0) {
+ len = -1; // Bad source
+ dst[0] = '\0';
+ } else {
+ struct mg_str v = mg_http_var(*buf, mg_str(name));
+ if (v.ptr == NULL) {
+ len = -4; // Name does not exist
+ } else {
+ len = mg_url_decode(v.ptr, v.len, dst, dst_len, 1);
+ if (len < 0) len = -3; // Failed to decode
+ }
+ }
+ return len;
+}
+
+static bool isx(int c) {
+ return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
+ (c >= 'A' && c <= 'F');
+}
+
+int mg_url_decode(const char *src, size_t src_len, char *dst, size_t dst_len,
+ int is_form_url_encoded) {
+ size_t i, j;
+ for (i = j = 0; i < src_len && j + 1 < dst_len; i++, j++) {
+ if (src[i] == '%') {
+ // Use `i + 2 < src_len`, not `i < src_len - 2`, note small src_len
+ if (i + 2 < src_len && isx(src[i + 1]) && isx(src[i + 2])) {
+ mg_unhex(src + i + 1, 2, (uint8_t *) &dst[j]);
+ i += 2;
+ } else {
+ return -1;
+ }
+ } else if (is_form_url_encoded && src[i] == '+') {
+ dst[j] = ' ';
+ } else {
+ dst[j] = src[i];
+ }
+ }
+ if (j < dst_len) dst[j] = '\0'; // Null-terminate the destination
+ return i >= src_len && j < dst_len ? (int) j : -1;
+}
+
+static bool isok(uint8_t c) {
+ return c == '\n' || c == '\r' || c >= ' ';
+}
+
+int mg_http_get_request_len(const unsigned char *buf, size_t buf_len) {
+ size_t i;
+ for (i = 0; i < buf_len; i++) {
+ if (!isok(buf[i])) return -1;
+ if ((i > 0 && buf[i] == '\n' && buf[i - 1] == '\n') ||
+ (i > 3 && buf[i] == '\n' && buf[i - 1] == '\r' && buf[i - 2] == '\n'))
+ return (int) i + 1;
+ }
+ return 0;
+}
+
+static const char *skip(const char *s, const char *e, const char *d,
+ struct mg_str *v) {
+ v->ptr = s;
+ while (s < e && *s != '\n' && strchr(d, *s) == NULL) s++;
+ v->len = (size_t) (s - v->ptr);
+ while (s < e && strchr(d, *s) != NULL) s++;
+ return s;
+}
+
+struct mg_str *mg_http_get_header(struct mg_http_message *h, const char *name) {
+ size_t i, n = strlen(name), max = sizeof(h->headers) / sizeof(h->headers[0]);
+ for (i = 0; i < max && h->headers[i].name.len > 0; i++) {
+ struct mg_str *k = &h->headers[i].name, *v = &h->headers[i].value;
+ if (n == k->len && mg_ncasecmp(k->ptr, name, n) == 0) return v;
+ }
+ return NULL;
+}
+
+static void mg_http_parse_headers(const char *s, const char *end,
+ struct mg_http_header *h, int max_headers) {
+ int i;
+ for (i = 0; i < max_headers; i++) {
+ struct mg_str k, v, tmp;
+ const char *he = skip(s, end, "\n", &tmp);
+ s = skip(s, he, ": \r\n", &k);
+ s = skip(s, he, "\r\n", &v);
+ if (k.len == tmp.len) continue;
+ while (v.len > 0 && v.ptr[v.len - 1] == ' ') v.len--; // Trim spaces
+ if (k.len == 0) break;
+ // MG_INFO(("--HH [%.*s] [%.*s] [%.*s]", (int) tmp.len - 1, tmp.ptr,
+ //(int) k.len, k.ptr, (int) v.len, v.ptr));
+ h[i].name = k;
+ h[i].value = v;
+ }
+}
+
+int mg_http_parse(const char *s, size_t len, struct mg_http_message *hm) {
+ int is_response, req_len = mg_http_get_request_len((unsigned char *) s, len);
+ const char *end = s == NULL ? NULL : s + req_len, *qs; // Cannot add to NULL
+ struct mg_str *cl;
+
+ memset(hm, 0, sizeof(*hm));
+ if (req_len <= 0) return req_len;
+
+ hm->message.ptr = hm->head.ptr = s;
+ hm->body.ptr = end;
+ hm->head.len = (size_t) req_len;
+ hm->chunk.ptr = end;
+ hm->message.len = hm->body.len = (size_t) ~0; // Set body length to infinite
+
+ // Parse request line
+ s = skip(s, end, " ", &hm->method);
+ s = skip(s, end, " ", &hm->uri);
+ s = skip(s, end, "\r\n", &hm->proto);
+
+ // Sanity check. Allow protocol/reason to be empty
+ if (hm->method.len == 0 || hm->uri.len == 0) return -1;
+
+ // If URI contains '?' character, setup query string
+ if ((qs = (const char *) memchr(hm->uri.ptr, '?', hm->uri.len)) != NULL) {
+ hm->query.ptr = qs + 1;
+ hm->query.len = (size_t) (&hm->uri.ptr[hm->uri.len] - (qs + 1));
+ hm->uri.len = (size_t) (qs - hm->uri.ptr);
+ }
+
+ mg_http_parse_headers(s, end, hm->headers,
+ sizeof(hm->headers) / sizeof(hm->headers[0]));
+ if ((cl = mg_http_get_header(hm, "Content-Length")) != NULL) {
+ if (mg_to_size_t(*cl, &hm->body.len) == false) return -1;
+ hm->message.len = (size_t) req_len + hm->body.len;
+ }
+
+ // mg_http_parse() is used to parse both HTTP requests and HTTP
+ // responses. If HTTP response does not have Content-Length set, then
+ // body is read until socket is closed, i.e. body.len is infinite (~0).
+ //
+ // For HTTP requests though, according to
+ // http://tools.ietf.org/html/rfc7231#section-8.1.3,
+ // only POST and PUT methods have defined body semantics.
+ // Therefore, if Content-Length is not specified and methods are
+ // not one of PUT or POST, set body length to 0.
+ //
+ // So, if it is HTTP request, and Content-Length is not set,
+ // and method is not (PUT or POST) then reset body length to zero.
+ is_response = mg_ncasecmp(hm->method.ptr, "HTTP/", 5) == 0;
+ if (hm->body.len == (size_t) ~0 && !is_response &&
+ mg_vcasecmp(&hm->method, "PUT") != 0 &&
+ mg_vcasecmp(&hm->method, "POST") != 0) {
+ hm->body.len = 0;
+ hm->message.len = (size_t) req_len;
+ }
+
+ // The 204 (No content) responses also have 0 body length
+ if (hm->body.len == (size_t) ~0 && is_response &&
+ mg_vcasecmp(&hm->uri, "204") == 0) {
+ hm->body.len = 0;
+ hm->message.len = (size_t) req_len;
+ }
+
+ return req_len;
+}
+
+static void mg_http_vprintf_chunk(struct mg_connection *c, const char *fmt,
+ va_list *ap) {
+ size_t len = c->send.len;
+ mg_send(c, " \r\n", 10);
+ mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, ap);
+ if (c->send.len >= len + 10) {
+ mg_snprintf((char *) c->send.buf + len, 9, "%08lx", c->send.len - len - 10);
+ c->send.buf[len + 8] = '\r';
+ if (c->send.len == len + 10) c->is_resp = 0; // Last chunk, reset marker
+ }
+ mg_send(c, "\r\n", 2);
+}
+
+void mg_http_printf_chunk(struct mg_connection *c, const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ mg_http_vprintf_chunk(c, fmt, &ap);
+ va_end(ap);
+}
+
+void mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len) {
+ mg_printf(c, "%lx\r\n", (unsigned long) len);
+ mg_send(c, buf, len);
+ mg_send(c, "\r\n", 2);
+ if (len == 0) c->is_resp = 0;
+}
+
+// clang-format off
+static const char *mg_http_status_code_str(int status_code) {
+ switch (status_code) {
+ case 100: return "Continue";
+ case 201: return "Created";
+ case 202: return "Accepted";
+ case 204: return "No Content";
+ case 206: return "Partial Content";
+ case 301: return "Moved Permanently";
+ case 302: return "Found";
+ case 304: return "Not Modified";
+ case 400: return "Bad Request";
+ case 401: return "Unauthorized";
+ case 403: return "Forbidden";
+ case 404: return "Not Found";
+ case 418: return "I'm a teapot";
+ case 500: return "Internal Server Error";
+ case 501: return "Not Implemented";
+ default: return "OK";
+ }
+}
+// clang-format on
+
+void mg_http_reply(struct mg_connection *c, int code, const char *headers,
+ const char *fmt, ...) {
+ va_list ap;
+ size_t len;
+ mg_printf(c, "HTTP/1.1 %d %s\r\n%sContent-Length: \r\n\r\n", code,
+ mg_http_status_code_str(code), headers == NULL ? "" : headers);
+ len = c->send.len;
+ va_start(ap, fmt);
+ mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, &ap);
+ va_end(ap);
+ if (c->send.len > 16) {
+ size_t n = mg_snprintf((char *) &c->send.buf[len - 15], 11, "%-10lu",
+ (unsigned long) (c->send.len - len));
+ c->send.buf[len - 15 + n] = ' '; // Change ending 0 to space
+ }
+ c->is_resp = 0;
+}
+
+static void http_cb(struct mg_connection *, int, void *, void *);
+static void restore_http_cb(struct mg_connection *c) {
+ mg_fs_close((struct mg_fd *) c->pfn_data);
+ c->pfn_data = NULL;
+ c->pfn = http_cb;
+ c->is_resp = 0;
+}
+
+char *mg_http_etag(char *buf, size_t len, size_t size, time_t mtime);
+char *mg_http_etag(char *buf, size_t len, size_t size, time_t mtime) {
+ mg_snprintf(buf, len, "\"%lld.%lld\"", (int64_t) mtime, (int64_t) size);
+ return buf;
+}
+
+static void static_cb(struct mg_connection *c, int ev, void *ev_data,
+ void *fn_data) {
+ if (ev == MG_EV_WRITE || ev == MG_EV_POLL) {
+ struct mg_fd *fd = (struct mg_fd *) fn_data;
+ // Read to send IO buffer directly, avoid extra on-stack buffer
+ size_t n, max = MG_IO_SIZE, space;
+ size_t *cl = (size_t *) &c->data[(sizeof(c->data) - sizeof(size_t)) /
+ sizeof(size_t) * sizeof(size_t)];
+ if (c->send.size < max) mg_iobuf_resize(&c->send, max);
+ if (c->send.len >= c->send.size) return; // Rate limit
+ if ((space = c->send.size - c->send.len) > *cl) space = *cl;
+ n = fd->fs->rd(fd->fd, c->send.buf + c->send.len, space);
+ c->send.len += n;
+ *cl -= n;
+ if (n == 0) restore_http_cb(c);
+ } else if (ev == MG_EV_CLOSE) {
+ restore_http_cb(c);
+ }
+ (void) ev_data;
+}
+
+// Known mime types. Keep it outside guess_content_type() function, since
+// some environments don't like it defined there.
+// clang-format off
+static struct mg_str s_known_types[] = {
+ MG_C_STR("html"), MG_C_STR("text/html; charset=utf-8"),
+ MG_C_STR("htm"), MG_C_STR("text/html; charset=utf-8"),
+ MG_C_STR("css"), MG_C_STR("text/css; charset=utf-8"),
+ MG_C_STR("js"), MG_C_STR("text/javascript; charset=utf-8"),
+ MG_C_STR("gif"), MG_C_STR("image/gif"),
+ MG_C_STR("png"), MG_C_STR("image/png"),
+ MG_C_STR("jpg"), MG_C_STR("image/jpeg"),
+ MG_C_STR("jpeg"), MG_C_STR("image/jpeg"),
+ MG_C_STR("woff"), MG_C_STR("font/woff"),
+ MG_C_STR("ttf"), MG_C_STR("font/ttf"),
+ MG_C_STR("svg"), MG_C_STR("image/svg+xml"),
+ MG_C_STR("txt"), MG_C_STR("text/plain; charset=utf-8"),
+ MG_C_STR("avi"), MG_C_STR("video/x-msvideo"),
+ MG_C_STR("csv"), MG_C_STR("text/csv"),
+ MG_C_STR("doc"), MG_C_STR("application/msword"),
+ MG_C_STR("exe"), MG_C_STR("application/octet-stream"),
+ MG_C_STR("gz"), MG_C_STR("application/gzip"),
+ MG_C_STR("ico"), MG_C_STR("image/x-icon"),
+ MG_C_STR("json"), MG_C_STR("application/json"),
+ MG_C_STR("mov"), MG_C_STR("video/quicktime"),
+ MG_C_STR("mp3"), MG_C_STR("audio/mpeg"),
+ MG_C_STR("mp4"), MG_C_STR("video/mp4"),
+ MG_C_STR("mpeg"), MG_C_STR("video/mpeg"),
+ MG_C_STR("pdf"), MG_C_STR("application/pdf"),
+ MG_C_STR("shtml"), MG_C_STR("text/html; charset=utf-8"),
+ MG_C_STR("tgz"), MG_C_STR("application/tar-gz"),
+ MG_C_STR("wav"), MG_C_STR("audio/wav"),
+ MG_C_STR("webp"), MG_C_STR("image/webp"),
+ MG_C_STR("zip"), MG_C_STR("application/zip"),
+ MG_C_STR("3gp"), MG_C_STR("video/3gpp"),
+ {0, 0},
+};
+// clang-format on
+
+static struct mg_str guess_content_type(struct mg_str path, const char *extra) {
+ struct mg_str k, v, s = mg_str(extra);
+ size_t i = 0;
+
+ // Shrink path to its extension only
+ while (i < path.len && path.ptr[path.len - i - 1] != '.') i++;
+ path.ptr += path.len - i;
+ path.len = i;
+
+ // Process user-provided mime type overrides, if any
+ while (mg_commalist(&s, &k, &v)) {
+ if (mg_strcmp(path, k) == 0) return v;
+ }
+
+ // Process built-in mime types
+ for (i = 0; s_known_types[i].ptr != NULL; i += 2) {
+ if (mg_strcmp(path, s_known_types[i]) == 0) return s_known_types[i + 1];
+ }
+
+ return mg_str("text/plain; charset=utf-8");
+}
+
+static int getrange(struct mg_str *s, size_t *a, size_t *b) {
+ size_t i, numparsed = 0;
+ // MG_INFO(("%.*s", (int) s->len, s->ptr));
+ for (i = 0; i + 6 < s->len; i++) {
+ if (memcmp(&s->ptr[i], "bytes=", 6) == 0) {
+ struct mg_str p = mg_str_n(s->ptr + i + 6, s->len - i - 6);
+ if (p.len > 0 && p.ptr[0] >= '0' && p.ptr[0] <= '9') numparsed++;
+ if (!mg_to_size_t(p, a)) return 0;
+ // MG_INFO(("PPP [%.*s] %d", (int) p.len, p.ptr, numparsed));
+ while (p.len && p.ptr[0] >= '0' && p.ptr[0] <= '9') p.ptr++, p.len--;
+ if (p.len && p.ptr[0] == '-') p.ptr++, p.len--;
+ if (!mg_to_size_t(p, b)) return 0;
+ if (p.len > 0 && p.ptr[0] >= '0' && p.ptr[0] <= '9') numparsed++;
+ // MG_INFO(("PPP [%.*s] %d", (int) p.len, p.ptr, numparsed));
+ break;
+ }
+ }
+ return (int) numparsed;
+}
+
+void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
+ const char *path,
+ const struct mg_http_serve_opts *opts) {
+ char etag[64], tmp[MG_PATH_MAX];
+ struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs;
+ struct mg_fd *fd = NULL;
+ size_t size = 0;
+ time_t mtime = 0;
+ struct mg_str *inm = NULL;
+ struct mg_str mime = guess_content_type(mg_str(path), opts->mime_types);
+ bool gzip = false;
+
+ if (path != NULL) {
+ // If a browser sends us "Accept-Encoding: gzip", try to open .gz first
+ struct mg_str *ae = mg_http_get_header(hm, "Accept-Encoding");
+ if (ae != NULL && mg_strstr(*ae, mg_str("gzip")) != NULL) {
+ mg_snprintf(tmp, sizeof(tmp), "%s.gz", path);
+ fd = mg_fs_open(fs, tmp, MG_FS_READ);
+ if (fd != NULL) gzip = true, path = tmp;
+ }
+ // No luck opening .gz? Open what we've told to open
+ if (fd == NULL) fd = mg_fs_open(fs, path, MG_FS_READ);
+ }
+
+ // Failed to open, and page404 is configured? Open it, then
+ if (fd == NULL && opts->page404 != NULL) {
+ fd = mg_fs_open(fs, opts->page404, MG_FS_READ);
+ mime = guess_content_type(mg_str(path), opts->mime_types);
+ path = opts->page404;
+ }
+
+ if (fd == NULL || fs->st(path, &size, &mtime) == 0) {
+ mg_http_reply(c, 404, opts->extra_headers, "Not found\n");
+ mg_fs_close(fd);
+ // NOTE: mg_http_etag() call should go first!
+ } else if (mg_http_etag(etag, sizeof(etag), size, mtime) != NULL &&
+ (inm = mg_http_get_header(hm, "If-None-Match")) != NULL &&
+ mg_vcasecmp(inm, etag) == 0) {
+ mg_fs_close(fd);
+ mg_http_reply(c, 304, opts->extra_headers, "");
+ } else {
+ int n, status = 200;
+ char range[100];
+ size_t r1 = 0, r2 = 0, cl = size;
+
+ // Handle Range header
+ struct mg_str *rh = mg_http_get_header(hm, "Range");
+ range[0] = '\0';
+ if (rh != NULL && (n = getrange(rh, &r1, &r2)) > 0) {
+ // If range is specified like "400-", set second limit to content len
+ if (n == 1) r2 = cl - 1;
+ if (r1 > r2 || r2 >= cl) {
+ status = 416;
+ cl = 0;
+ mg_snprintf(range, sizeof(range), "Content-Range: bytes */%lld\r\n",
+ (int64_t) size);
+ } else {
+ status = 206;
+ cl = r2 - r1 + 1;
+ mg_snprintf(range, sizeof(range),
+ "Content-Range: bytes %llu-%llu/%llu\r\n", (uint64_t) r1,
+ (uint64_t) (r1 + cl - 1), (uint64_t) size);
+ fs->sk(fd->fd, r1);
+ }
+ }
+ mg_printf(c,
+ "HTTP/1.1 %d %s\r\n"
+ "Content-Type: %.*s\r\n"
+ "Etag: %s\r\n"
+ "Content-Length: %llu\r\n"
+ "%s%s%s\r\n",
+ status, mg_http_status_code_str(status), (int) mime.len, mime.ptr,
+ etag, (uint64_t) cl, gzip ? "Content-Encoding: gzip\r\n" : "",
+ range, opts->extra_headers ? opts->extra_headers : "");
+ if (mg_vcasecmp(&hm->method, "HEAD") == 0) {
+ c->is_draining = 1;
+ c->is_resp = 0;
+ mg_fs_close(fd);
+ } else {
+ // Track to-be-sent content length at the end of c->data, aligned
+ size_t *clp = (size_t *) &c->data[(sizeof(c->data) - sizeof(size_t)) /
+ sizeof(size_t) * sizeof(size_t)];
+ c->pfn = static_cb;
+ c->pfn_data = fd;
+ *clp = cl;
+ }
+ }
+}
+
+struct printdirentrydata {
+ struct mg_connection *c;
+ struct mg_http_message *hm;
+ const struct mg_http_serve_opts *opts;
+ const char *dir;
+};
+
+static void printdirentry(const char *name, void *userdata) {
+ struct printdirentrydata *d = (struct printdirentrydata *) userdata;
+ struct mg_fs *fs = d->opts->fs == NULL ? &mg_fs_posix : d->opts->fs;
+ size_t size = 0;
+ time_t t = 0;
+ char path[MG_PATH_MAX], sz[40], mod[40];
+ int flags, n = 0;
+
+ // MG_DEBUG(("[%s] [%s]", d->dir, name));
+ if (mg_snprintf(path, sizeof(path), "%s%c%s", d->dir, '/', name) >
+ sizeof(path)) {
+ MG_ERROR(("%s truncated", name));
+ } else if ((flags = fs->st(path, &size, &t)) == 0) {
+ MG_ERROR(("%lu stat(%s): %d", d->c->id, path, errno));
+ } else {
+ const char *slash = flags & MG_FS_DIR ? "/" : "";
+ if (flags & MG_FS_DIR) {
+ mg_snprintf(sz, sizeof(sz), "%s", "[DIR]");
+ } else {
+ mg_snprintf(sz, sizeof(sz), "%lld", (uint64_t) size);
+ }
+#if defined(MG_HTTP_DIRLIST_TIME_FMT)
+ {
+ char time_str[40];
+ struct tm *time_info = localtime(&t);
+ strftime(time_str, sizeof time_str, "%Y/%m/%d %H:%M:%S", time_info);
+ mg_snprintf(mod, sizeof(mod), "%s", time_str);
+ }
+#else
+ mg_snprintf(mod, sizeof(mod), "%lu", (unsigned long) t);
+#endif
+ n = (int) mg_url_encode(name, strlen(name), path, sizeof(path));
+ mg_printf(d->c,
+ " <tr><td><a href=\"%.*s%s\">%s%s</a></td>"
+ "<td name=%lu>%s</td><td name=%lld>%s</td></tr>\n",
+ n, path, slash, name, slash, (unsigned long) t, mod,
+ flags & MG_FS_DIR ? (int64_t) -1 : (int64_t) size, sz);
+ }
+}
+
+static void listdir(struct mg_connection *c, struct mg_http_message *hm,
+ const struct mg_http_serve_opts *opts, char *dir) {
+ const char *sort_js_code =
+ "<script>function srt(tb, sc, so, d) {"
+ "var tr = Array.prototype.slice.call(tb.rows, 0),"
+ "tr = tr.sort(function (a, b) { var c1 = a.cells[sc], c2 = b.cells[sc],"
+ "n1 = c1.getAttribute('name'), n2 = c2.getAttribute('name'), "
+ "t1 = a.cells[2].getAttribute('name'), "
+ "t2 = b.cells[2].getAttribute('name'); "
+ "return so * (t1 < 0 && t2 >= 0 ? -1 : t2 < 0 && t1 >= 0 ? 1 : "
+ "n1 ? parseInt(n2) - parseInt(n1) : "
+ "c1.textContent.trim().localeCompare(c2.textContent.trim())); });";
+ const char *sort_js_code2 =
+ "for (var i = 0; i < tr.length; i++) tb.appendChild(tr[i]); "
+ "if (!d) window.location.hash = ('sc=' + sc + '&so=' + so); "
+ "};"
+ "window.onload = function() {"
+ "var tb = document.getElementById('tb');"
+ "var m = /sc=([012]).so=(1|-1)/.exec(window.location.hash) || [0, 2, 1];"
+ "var sc = m[1], so = m[2]; document.onclick = function(ev) { "
+ "var c = ev.target.rel; if (c) {if (c == sc) so *= -1; srt(tb, c, so); "
+ "sc = c; ev.preventDefault();}};"
+ "srt(tb, sc, so, true);"
+ "}"
+ "</script>";
+ struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs;
+ struct printdirentrydata d = {c, hm, opts, dir};
+ char tmp[10], buf[MG_PATH_MAX];
+ size_t off, n;
+ int len = mg_url_decode(hm->uri.ptr, hm->uri.len, buf, sizeof(buf), 0);
+ struct mg_str uri = len > 0 ? mg_str_n(buf, (size_t) len) : hm->uri;
+
+ mg_printf(c,
+ "HTTP/1.1 200 OK\r\n"
+ "Content-Type: text/html; charset=utf-8\r\n"
+ "%s"
+ "Content-Length: \r\n\r\n",
+ opts->extra_headers == NULL ? "" : opts->extra_headers);
+ off = c->send.len; // Start of body
+ mg_printf(c,
+ "<!DOCTYPE html><html><head><title>Index of %.*s</title>%s%s"
+ "<style>th,td {text-align: left; padding-right: 1em; "
+ "font-family: monospace; }</style></head>"
+ "<body><h1>Index of %.*s</h1><table cellpadding=\"0\"><thead>"
+ "<tr><th><a href=\"#\" rel=\"0\">Name</a></th><th>"
+ "<a href=\"#\" rel=\"1\">Modified</a></th>"
+ "<th><a href=\"#\" rel=\"2\">Size</a></th></tr>"
+ "<tr><td colspan=\"3\"><hr></td></tr>"
+ "</thead>"
+ "<tbody id=\"tb\">\n",
+ (int) uri.len, uri.ptr, sort_js_code, sort_js_code2, (int) uri.len,
+ uri.ptr);
+ mg_printf(c, "%s",
+ " <tr><td><a href=\"..\">..</a></td>"
+ "<td name=-1></td><td name=-1>[DIR]</td></tr>\n");
+
+ fs->ls(dir, printdirentry, &d);
+ mg_printf(c,
+ "</tbody><tfoot><tr><td colspan=\"3\"><hr></td></tr></tfoot>"
+ "</table><address>Mongoose v.%s</address></body></html>\n",
+ MG_VERSION);
+ n = mg_snprintf(tmp, sizeof(tmp), "%lu", (unsigned long) (c->send.len - off));
+ if (n > sizeof(tmp)) n = 0;
+ memcpy(c->send.buf + off - 12, tmp, n); // Set content length
+ c->is_resp = 0; // Mark response end
+}
+
+// Resolve requested file into `path` and return its fs->st() result
+static int uri_to_path2(struct mg_connection *c, struct mg_http_message *hm,
+ struct mg_fs *fs, struct mg_str url, struct mg_str dir,
+ char *path, size_t path_size) {
+ int flags, tmp;
+ // Append URI to the root_dir, and sanitize it
+ size_t n = mg_snprintf(path, path_size, "%.*s", (int) dir.len, dir.ptr);
+ if (n > path_size) n = path_size;
+ path[path_size - 1] = '\0';
+ if (n + 2 < path_size) path[n++] = '/', path[n] = '\0';
+ mg_url_decode(hm->uri.ptr + url.len, hm->uri.len - url.len, path + n,
+ path_size - n, 0);
+ path[path_size - 1] = '\0'; // Double-check
+ mg_remove_double_dots(path);
+ n = strlen(path);
+ while (n > 1 && path[n - 1] == '/') path[--n] = 0; // Trim trailing slashes
+ flags = mg_vcmp(&hm->uri, "/") == 0 ? MG_FS_DIR : fs->st(path, NULL, NULL);
+ MG_VERBOSE(("%lu %.*s -> %s %d", c->id, (int) hm->uri.len, hm->uri.ptr, path,
+ flags));
+ if (flags == 0) {
+ // Do nothing - let's caller decide
+ } else if ((flags & MG_FS_DIR) && hm->uri.len > 0 &&
+ hm->uri.ptr[hm->uri.len - 1] != '/') {
+ mg_printf(c,
+ "HTTP/1.1 301 Moved\r\n"
+ "Location: %.*s/\r\n"
+ "Content-Length: 0\r\n"
+ "\r\n",
+ (int) hm->uri.len, hm->uri.ptr);
+ c->is_resp = 0;
+ flags = -1;
+ } else if (flags & MG_FS_DIR) {
+ if (((mg_snprintf(path + n, path_size - n, "/" MG_HTTP_INDEX) > 0 &&
+ (tmp = fs->st(path, NULL, NULL)) != 0) ||
+ (mg_snprintf(path + n, path_size - n, "/index.shtml") > 0 &&
+ (tmp = fs->st(path, NULL, NULL)) != 0))) {
+ flags = tmp;
+ } else if ((mg_snprintf(path + n, path_size - n, "/" MG_HTTP_INDEX ".gz") >
+ 0 &&
+ (tmp = fs->st(path, NULL, NULL)) !=
+ 0)) { // check for gzipped index
+ flags = tmp;
+ path[n + 1 + strlen(MG_HTTP_INDEX)] =
+ '\0'; // Remove appended .gz in index file name
+ } else {
+ path[n] = '\0'; // Remove appended index file name
+ }
+ }
+ return flags;
+}
+
+static int uri_to_path(struct mg_connection *c, struct mg_http_message *hm,
+ const struct mg_http_serve_opts *opts, char *path,
+ size_t path_size) {
+ struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs;
+ struct mg_str k, v, s = mg_str(opts->root_dir), u = {0, 0}, p = {0, 0};
+ while (mg_commalist(&s, &k, &v)) {
+ if (v.len == 0) v = k, k = mg_str("/");
+ if (hm->uri.len < k.len) continue;
+ if (mg_strcmp(k, mg_str_n(hm->uri.ptr, k.len)) != 0) continue;
+ u = k, p = v;
+ }
+ return uri_to_path2(c, hm, fs, u, p, path, path_size);
+}
+
+void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
+ const struct mg_http_serve_opts *opts) {
+ char path[MG_PATH_MAX];
+ const char *sp = opts->ssi_pattern;
+ int flags = uri_to_path(c, hm, opts, path, sizeof(path));
+ if (flags < 0) {
+ // Do nothing: the response has already been sent by uri_to_path()
+ } else if (flags & MG_FS_DIR) {
+ listdir(c, hm, opts, path);
+ } else if (flags && sp != NULL &&
+ mg_globmatch(sp, strlen(sp), path, strlen(path))) {
+ mg_http_serve_ssi(c, opts->root_dir, path);
+ } else {
+ mg_http_serve_file(c, hm, path, opts);
+ }
+}
+
+static bool mg_is_url_safe(int c) {
+ return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') || c == '.' || c == '_' || c == '-' || c == '~';
+}
+
+size_t mg_url_encode(const char *s, size_t sl, char *buf, size_t len) {
+ size_t i, n = 0;
+ for (i = 0; i < sl; i++) {
+ int c = *(unsigned char *) &s[i];
+ if (n + 4 >= len) return 0;
+ if (mg_is_url_safe(c)) {
+ buf[n++] = s[i];
+ } else {
+ buf[n++] = '%';
+ mg_hex(&s[i], 1, &buf[n]);
+ n += 2;
+ }
+ }
+ if (len > 0 && n < len - 1) buf[n] = '\0'; // Null-terminate the destination
+ if (len > 0) buf[len - 1] = '\0'; // Always.
+ return n;
+}
+
+void mg_http_creds(struct mg_http_message *hm, char *user, size_t userlen,
+ char *pass, size_t passlen) {
+ struct mg_str *v = mg_http_get_header(hm, "Authorization");
+ user[0] = pass[0] = '\0';
+ if (v != NULL && v->len > 6 && memcmp(v->ptr, "Basic ", 6) == 0) {
+ char buf[256];
+ int n = mg_base64_decode(v->ptr + 6, (int) v->len - 6, buf);
+ const char *p = (const char *) memchr(buf, ':', n > 0 ? (size_t) n : 0);
+ if (p != NULL) {
+ mg_snprintf(user, userlen, "%.*s", (int) (p - buf), buf);
+ mg_snprintf(pass, passlen, "%.*s", n - (int) (p - buf) - 1, p + 1);
+ }
+ } else if (v != NULL && v->len > 7 && memcmp(v->ptr, "Bearer ", 7) == 0) {
+ mg_snprintf(pass, passlen, "%.*s", (int) v->len - 7, v->ptr + 7);
+ } else if ((v = mg_http_get_header(hm, "Cookie")) != NULL) {
+ struct mg_str t = mg_http_get_header_var(*v, mg_str_n("access_token", 12));
+ if (t.len > 0) mg_snprintf(pass, passlen, "%.*s", (int) t.len, t.ptr);
+ } else {
+ mg_http_get_var(&hm->query, "access_token", pass, passlen);
+ }
+}
+
+static struct mg_str stripquotes(struct mg_str s) {
+ return s.len > 1 && s.ptr[0] == '"' && s.ptr[s.len - 1] == '"'
+ ? mg_str_n(s.ptr + 1, s.len - 2)
+ : s;
+}
+
+struct mg_str mg_http_get_header_var(struct mg_str s, struct mg_str v) {
+ size_t i;
+ for (i = 0; v.len > 0 && i + v.len + 2 < s.len; i++) {
+ if (s.ptr[i + v.len] == '=' && memcmp(&s.ptr[i], v.ptr, v.len) == 0) {
+ const char *p = &s.ptr[i + v.len + 1], *b = p, *x = &s.ptr[s.len];
+ int q = p < x && *p == '"' ? 1 : 0;
+ while (p < x &&
+ (q ? p == b || *p != '"' : *p != ';' && *p != ' ' && *p != ','))
+ p++;
+ // MG_INFO(("[%.*s] [%.*s] [%.*s]", (int) s.len, s.ptr, (int) v.len,
+ // v.ptr, (int) (p - b), b));
+ return stripquotes(mg_str_n(b, (size_t) (p - b + q)));
+ }
+ }
+ return mg_str_n(NULL, 0);
+}
+
+bool mg_http_match_uri(const struct mg_http_message *hm, const char *glob) {
+ return mg_match(hm->uri, mg_str(glob), NULL);
+}
+
+long mg_http_upload(struct mg_connection *c, struct mg_http_message *hm,
+ struct mg_fs *fs, const char *path, size_t max_size) {
+ char buf[20] = "0";
+ long res = 0, offset;
+ mg_http_get_var(&hm->query, "offset", buf, sizeof(buf));
+ offset = strtol(buf, NULL, 0);
+ if (hm->body.len == 0) {
+ mg_http_reply(c, 200, "", "%ld", res); // Nothing to write
+ } else {
+ struct mg_fd *fd;
+ size_t current_size = 0;
+ MG_DEBUG(("%s -> %d bytes @ %ld", path, (int) hm->body.len, offset));
+ if (offset == 0) fs->rm(path); // If offset if 0, truncate file
+ fs->st(path, ¤t_size, NULL);
+ if (offset < 0) {
+ mg_http_reply(c, 400, "", "offset required");
+ res = -1;
+ } else if (offset > 0 && current_size != (size_t) offset) {
+ mg_http_reply(c, 400, "", "%s: offset mismatch", path);
+ res = -2;
+ } else if ((size_t) offset + hm->body.len > max_size) {
+ mg_http_reply(c, 400, "", "%s: over max size of %lu", path,
+ (unsigned long) max_size);
+ res = -3;
+ } else if ((fd = mg_fs_open(fs, path, MG_FS_WRITE)) == NULL) {
+ mg_http_reply(c, 400, "", "open(%s): %d", path, errno);
+ res = -4;
+ } else {
+ res = offset + (long) fs->wr(fd->fd, hm->body.ptr, hm->body.len);
+ mg_fs_close(fd);
+ mg_http_reply(c, 200, "", "%ld", res);
+ }
+ }
+ return res;
+}
+
+int mg_http_status(const struct mg_http_message *hm) {
+ return atoi(hm->uri.ptr);
+}
+
+// If a server sends data to the client using chunked encoding, Mongoose strips
+// off the chunking prefix (hex length and \r\n) and suffix (\r\n), appends the
+// stripped data to the body, and fires the MG_EV_HTTP_CHUNK event. When zero
+// chunk is received, we fire MG_EV_HTTP_MSG, and the body already has all
+// chunking prefixes/suffixes stripped.
+//
+// If a server sends data without chunked encoding, we also fire a series of
+// MG_EV_HTTP_CHUNK events for every received piece of data, and then we fire
+// MG_EV_HTTP_MSG event in the end.
+//
+// We track total processed length in the c->pfn_data, which is a void *
+// pointer: we store a size_t value there.
+static bool getchunk(struct mg_str s, size_t *prefixlen, size_t *datalen) {
+ size_t i = 0, n;
+ while (i < s.len && s.ptr[i] != '\r' && s.ptr[i] != '\n') i++;
+ n = mg_unhexn(s.ptr, i);
+ // MG_INFO(("%d %d", (int) (i + n + 4), (int) s.len));
+ if (s.len < i + n + 4) return false; // Chunk not yet fully buffered
+ if (s.ptr[i] != '\r' || s.ptr[i + 1] != '\n') return false;
+ if (s.ptr[i + n + 2] != '\r' || s.ptr[i + n + 3] != '\n') return false;
+ *prefixlen = i + 2;
+ *datalen = n;
+ return true;
+}
+
+static bool mg_is_chunked(struct mg_http_message *hm) {
+ const char *needle = "chunked";
+ struct mg_str *te = mg_http_get_header(hm, "Transfer-Encoding");
+ return te != NULL && mg_vcasecmp(te, needle) == 0;
+}
+
+void mg_http_delete_chunk(struct mg_connection *c, struct mg_http_message *hm) {
+ size_t ofs = (size_t) (hm->chunk.ptr - (char *) c->recv.buf);
+ mg_iobuf_del(&c->recv, ofs, hm->chunk.len);
+ c->pfn_data = (void *) ((size_t) c->pfn_data | MG_DMARK);
+}
+
+static void deliver_chunked_chunks(struct mg_connection *c, size_t hlen,
+ struct mg_http_message *hm, bool *next) {
+ // | ... headers ... | HEXNUM\r\n ..data.. \r\n | ......
+ // +------------------+--------------------------+----
+ // | hlen | chunk1 | ......
+ char *buf = (char *) &c->recv.buf[hlen], *p = buf;
+ size_t len = c->recv.len - hlen;
+ size_t processed = ((size_t) c->pfn_data) & ~MG_DMARK;
+ size_t mark, pl, dl, del = 0, ofs = 0;
+ bool last = false;
+ if (processed <= len) len -= processed, buf += processed;
+ while (!last && getchunk(mg_str_n(buf + ofs, len - ofs), &pl, &dl)) {
+ size_t saved = c->recv.len;
+ memmove(p + processed, buf + ofs + pl, dl);
+ // MG_INFO(("P2 [%.*s]", (int) (processed + dl), p));
+ hm->chunk = mg_str_n(p + processed, dl);
+ mg_call(c, MG_EV_HTTP_CHUNK, hm);
+ ofs += pl + dl + 2, del += pl + 2; // 2 is for \r\n suffix
+ processed += dl;
+ if (c->recv.len != saved) processed -= dl, buf -= dl;
+ // mg_hexdump(c->recv.buf, hlen + processed);
+ last = (dl == 0);
+ }
+ mg_iobuf_del(&c->recv, hlen + processed, del);
+ mark = ((size_t) c->pfn_data) & MG_DMARK;
+ c->pfn_data = (void *) (processed | mark);
+ if (last) {
+ hm->body.len = processed;
+ hm->message.len = hlen + processed;
+ c->pfn_data = NULL;
+ if (mark) mg_iobuf_del(&c->recv, 0, hlen), *next = true;
+ // MG_INFO(("LAST, mark: %lx", mark));
+ // mg_hexdump(c->recv.buf, c->recv.len);
+ }
+}
+
+static void deliver_normal_chunks(struct mg_connection *c, size_t hlen,
+ struct mg_http_message *hm, bool *next) {
+ size_t left, processed = ((size_t) c->pfn_data) & ~MG_DMARK;
+ size_t deleted = ((size_t) c->pfn_data) & MG_DMARK;
+ hm->chunk = mg_str_n((char *) &c->recv.buf[hlen], c->recv.len - hlen);
+ if (processed <= hm->chunk.len && !deleted) {
+ hm->chunk.len -= processed;
+ hm->chunk.ptr += processed;
+ }
+ left = hm->body.len < processed ? 0 : hm->body.len - processed;
+ if (hm->chunk.len > left) hm->chunk.len = left;
+ if (hm->chunk.len > 0) mg_call(c, MG_EV_HTTP_CHUNK, hm);
+ processed += hm->chunk.len;
+ deleted = ((size_t) c->pfn_data) & MG_DMARK; // Re-evaluate after user call
+ if (processed >= hm->body.len) { // Last, 0-len chunk
+ hm->chunk.len = 0; // Reset length
+ mg_call(c, MG_EV_HTTP_CHUNK, hm); // Call user handler
+ c->pfn_data = NULL; // Reset processed counter
+ if (processed && deleted) mg_iobuf_del(&c->recv, 0, hlen), *next = true;
+ } else {
+ c->pfn_data = (void *) (processed | deleted); // if it is set
+ }
+}
+
+static void http_cb(struct mg_connection *c, int ev, void *evd, void *fnd) {
+ if (ev == MG_EV_READ || ev == MG_EV_CLOSE) {
+ struct mg_http_message hm;
+ // mg_hexdump(c->recv.buf, c->recv.len);
+ while (c->recv.buf != NULL && c->recv.len > 0) {
+ bool next = false;
+ int hlen = mg_http_parse((char *) c->recv.buf, c->recv.len, &hm);
+ if (hlen < 0) {
+ mg_error(c, "HTTP parse:\n%.*s", (int) c->recv.len, c->recv.buf);
+ break;
+ }
+ if (c->is_resp) break; // Response is still generated
+ if (hlen == 0) break; // Request is not buffered yet
+ if (ev == MG_EV_CLOSE) { // If client did not set Content-Length
+ hm.message.len = c->recv.len; // and closes now, deliver a MSG
+ hm.body.len = hm.message.len - (size_t) (hm.body.ptr - hm.message.ptr);
+ }
+ if (mg_is_chunked(&hm)) {
+ deliver_chunked_chunks(c, (size_t) hlen, &hm, &next);
+ } else {
+ deliver_normal_chunks(c, (size_t) hlen, &hm, &next);
+ }
+ if (next) continue; // Chunks & request were deleted
+ // Chunk events are delivered. If we have full body, deliver MSG
+ if (c->recv.len < hm.message.len) break;
+ if (c->is_accepted) c->is_resp = 1; // Start generating response
+ mg_call(c, MG_EV_HTTP_MSG, &hm); // User handler can clear is_resp
+ mg_iobuf_del(&c->recv, 0, hm.message.len);
+ }
+ }
+ (void) evd, (void) fnd;
+}
+
+static void mg_hfn(struct mg_connection *c, int ev, void *ev_data, void *fnd) {
+ if (ev == MG_EV_HTTP_MSG) {
+ struct mg_http_message *hm = (struct mg_http_message *) ev_data;
+ if (mg_http_match_uri(hm, "/quit")) {
+ mg_http_reply(c, 200, "", "ok\n");
+ c->is_draining = 1;
+ c->data[0] = 'X';
+ } else if (mg_http_match_uri(hm, "/debug")) {
+ int level = (int) mg_json_get_long(hm->body, "$.level", MG_LL_DEBUG);
+ mg_log_set(level);
+ mg_http_reply(c, 200, "", "Debug level set to %d\n", level);
+ } else {
+ mg_http_reply(c, 200, "", "hi\n");
+ }
+ } else if (ev == MG_EV_CLOSE) {
+ if (c->data[0] == 'X') *(bool *) fnd = true;
+ }
+}
+
+void mg_hello(const char *url) {
+ struct mg_mgr mgr;
+ bool done = false;
+ mg_mgr_init(&mgr);
+ if (mg_http_listen(&mgr, url, mg_hfn, &done) == NULL) done = true;
+ while (done == false) mg_mgr_poll(&mgr, 100);
+ mg_mgr_free(&mgr);
+}
+
+struct mg_connection *mg_http_connect(struct mg_mgr *mgr, const char *url,
+ mg_event_handler_t fn, void *fn_data) {
+ struct mg_connection *c = mg_connect(mgr, url, fn, fn_data);
+ if (c != NULL) c->pfn = http_cb;
+ return c;
+}
+
+struct mg_connection *mg_http_listen(struct mg_mgr *mgr, const char *url,
+ mg_event_handler_t fn, void *fn_data) {
+ struct mg_connection *c = mg_listen(mgr, url, fn, fn_data);
+ if (c != NULL) c->pfn = http_cb;
+ return c;
+}
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/iobuf.c"
+#endif
+
+
+
+
+// Not using memset for zeroing memory, cause it can be dropped by compiler
+// See https://github.com/cesanta/mongoose/pull/1265
+static void zeromem(volatile unsigned char *buf, size_t len) {
+ if (buf != NULL) {
+ while (len--) *buf++ = 0;
+ }
+}
+
+static size_t roundup(size_t size, size_t align) {
+ return align == 0 ? size : (size + align - 1) / align * align;
+}
+
+int mg_iobuf_resize(struct mg_iobuf *io, size_t new_size) {
+ int ok = 1;
+ new_size = roundup(new_size, io->align);
+ if (new_size == 0) {
+ zeromem(io->buf, io->size);
+ free(io->buf);
+ io->buf = NULL;
+ io->len = io->size = 0;
+ } else if (new_size != io->size) {
+ // NOTE(lsm): do not use realloc here. Use calloc/free only, to ease the
+ // porting to some obscure platforms like FreeRTOS
+ void *p = calloc(1, new_size);
+ if (p != NULL) {
+ size_t len = new_size < io->len ? new_size : io->len;
+ if (len > 0 && io->buf != NULL) memmove(p, io->buf, len);
+ zeromem(io->buf, io->size);
+ free(io->buf);
+ io->buf = (unsigned char *) p;
+ io->size = new_size;
+ } else {
+ ok = 0;
+ MG_ERROR(("%lld->%lld", (uint64_t) io->size, (uint64_t) new_size));
+ }
+ }
+ return ok;
+}
+
+int mg_iobuf_init(struct mg_iobuf *io, size_t size, size_t align) {
+ io->buf = NULL;
+ io->align = align;
+ io->size = io->len = 0;
+ return mg_iobuf_resize(io, size);
+}
+
+size_t mg_iobuf_add(struct mg_iobuf *io, size_t ofs, const void *buf,
+ size_t len) {
+ size_t new_size = roundup(io->len + len, io->align);
+ mg_iobuf_resize(io, new_size); // Attempt to resize
+ if (new_size != io->size) len = 0; // Resize failure, append nothing
+ if (ofs < io->len) memmove(io->buf + ofs + len, io->buf + ofs, io->len - ofs);
+ if (buf != NULL) memmove(io->buf + ofs, buf, len);
+ if (ofs > io->len) io->len += ofs - io->len;
+ io->len += len;
+ return len;
+}
+
+size_t mg_iobuf_del(struct mg_iobuf *io, size_t ofs, size_t len) {
+ if (ofs > io->len) ofs = io->len;
+ if (ofs + len > io->len) len = io->len - ofs;
+ if (io->buf) memmove(io->buf + ofs, io->buf + ofs + len, io->len - ofs - len);
+ if (io->buf) zeromem(io->buf + io->len - len, len);
+ io->len -= len;
+ return len;
+}
+
+void mg_iobuf_free(struct mg_iobuf *io) {
+ mg_iobuf_resize(io, 0);
+}
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/json.c"
+#endif
+
+
+
+
+static const char *escapeseq(int esc) {
+ return esc ? "\b\f\n\r\t\\\"" : "bfnrt\\\"";
+}
+
+static char json_esc(int c, int esc) {
+ const char *p, *esc1 = escapeseq(esc), *esc2 = escapeseq(!esc);
+ for (p = esc1; *p != '\0'; p++) {
+ if (*p == c) return esc2[p - esc1];
+ }
+ return 0;
+}
+
+static int mg_pass_string(const char *s, int len) {
+ int i;
+ for (i = 0; i < len; i++) {
+ if (s[i] == '\\' && i + 1 < len && json_esc(s[i + 1], 1)) {
+ i++;
+ } else if (s[i] == '\0') {
+ return MG_JSON_INVALID;
+ } else if (s[i] == '"') {
+ return i;
+ }
+ }
+ return MG_JSON_INVALID;
+}
+
+static double mg_atod(const char *p, int len, int *numlen) {
+ double d = 0.0;
+ int i = 0, sign = 1;
+
+ // Sign
+ if (i < len && *p == '-') {
+ sign = -1, i++;
+ } else if (i < len && *p == '+') {
+ i++;
+ }
+
+ // Decimal
+ for (; i < len && p[i] >= '0' && p[i] <= '9'; i++) {
+ d *= 10.0;
+ d += p[i] - '0';
+ }
+ d *= sign;
+
+ // Fractional
+ if (i < len && p[i] == '.') {
+ double frac = 0.0, base = 0.1;
+ i++;
+ for (; i < len && p[i] >= '0' && p[i] <= '9'; i++) {
+ frac += base * (p[i] - '0');
+ base /= 10.0;
+ }
+ d += frac * sign;
+ }
+
+ // Exponential
+ if (i < len && (p[i] == 'e' || p[i] == 'E')) {
+ int j, exp = 0, minus = 0;
+ i++;
+ if (i < len && p[i] == '-') minus = 1, i++;
+ if (i < len && p[i] == '+') i++;
+ while (i < len && p[i] >= '0' && p[i] <= '9' && exp < 308)
+ exp = exp * 10 + (p[i++] - '0');
+ if (minus) exp = -exp;
+ for (j = 0; j < exp; j++) d *= 10.0;
+ for (j = 0; j < -exp; j++) d /= 10.0;
+ }
+
+ if (numlen != NULL) *numlen = i;
+ return d;
+}
+
+int mg_json_get(struct mg_str json, const char *path, int *toklen) {
+ const char *s = json.ptr;
+ int len = (int) json.len;
+ enum { S_VALUE, S_KEY, S_COLON, S_COMMA_OR_EOO } expecting = S_VALUE;
+ unsigned char nesting[MG_JSON_MAX_DEPTH];
+ int i = 0; // Current offset in `s`
+ int j = 0; // Offset in `s` we're looking for (return value)
+ int depth = 0; // Current depth (nesting level)
+ int ed = 0; // Expected depth
+ int pos = 1; // Current position in `path`
+ int ci = -1, ei = -1; // Current and expected index in array
+
+ if (toklen) *toklen = 0;
+ if (path[0] != '$') return MG_JSON_INVALID;
+
+#define MG_CHECKRET(x) \
+ do { \
+ if (depth == ed && path[pos] == '\0' && ci == ei) { \
+ if (toklen) *toklen = i - j + 1; \
+ return j; \
+ } \
+ } while (0)
+
+// In the ascii table, the distance between `[` and `]` is 2.
+// Ditto for `{` and `}`. Hence +2 in the code below.
+#define MG_EOO(x) \
+ do { \
+ if (depth == ed && ci != ei) return MG_JSON_NOT_FOUND; \
+ if (c != nesting[depth - 1] + 2) return MG_JSON_INVALID; \
+ depth--; \
+ MG_CHECKRET(x); \
+ } while (0)
+
+ for (i = 0; i < len; i++) {
+ unsigned char c = ((unsigned char *) s)[i];
+ if (c == ' ' || c == '\t' || c == '\n' || c == '\r') continue;
+ switch (expecting) {
+ case S_VALUE:
+ // p("V %s [%.*s] %d %d %d %d\n", path, pos, path, depth, ed, ci, ei);
+ if (depth == ed) j = i;
+ if (c == '{') {
+ if (depth >= (int) sizeof(nesting)) return MG_JSON_TOO_DEEP;
+ if (depth == ed && path[pos] == '.' && ci == ei) {
+ // If we start the object, reset array indices
+ ed++, pos++, ci = ei = -1;
+ }
+ nesting[depth++] = c;
+ expecting = S_KEY;
+ break;
+ } else if (c == '[') {
+ if (depth >= (int) sizeof(nesting)) return MG_JSON_TOO_DEEP;
+ if (depth == ed && path[pos] == '[' && ei == ci) {
+ ed++, pos++, ci = 0;
+ for (ei = 0; path[pos] != ']' && path[pos] != '\0'; pos++) {
+ ei *= 10;
+ ei += path[pos] - '0';
+ }
+ if (path[pos] != 0) pos++;
+ }
+ nesting[depth++] = c;
+ break;
+ } else if (c == ']' && depth > 0) { // Empty array
+ MG_EOO(']');
+ } else if (c == 't' && i + 3 < len && memcmp(&s[i], "true", 4) == 0) {
+ i += 3;
+ } else if (c == 'n' && i + 3 < len && memcmp(&s[i], "null", 4) == 0) {
+ i += 3;
+ } else if (c == 'f' && i + 4 < len && memcmp(&s[i], "false", 5) == 0) {
+ i += 4;
+ } else if (c == '-' || ((c >= '0' && c <= '9'))) {
+ int numlen = 0;
+ mg_atod(&s[i], len - i, &numlen);
+ i += numlen - 1;
+ } else if (c == '"') {
+ int n = mg_pass_string(&s[i + 1], len - i - 1);
+ if (n < 0) return n;
+ i += n + 1;
+ } else {
+ return MG_JSON_INVALID;
+ }
+ MG_CHECKRET('V');
+ if (depth == ed && ei >= 0) ci++;
+ expecting = S_COMMA_OR_EOO;
+ break;
+
+ case S_KEY:
+ if (c == '"') {
+ int n = mg_pass_string(&s[i + 1], len - i - 1);
+ if (n < 0) return n;
+ if (i + 1 + n >= len) return MG_JSON_NOT_FOUND;
+ if (depth < ed) return MG_JSON_NOT_FOUND;
+ if (depth == ed && path[pos - 1] != '.') return MG_JSON_NOT_FOUND;
+ // printf("K %s [%.*s] [%.*s] %d %d %d\n", path, pos, path, n,
+ // &s[i + 1], n, depth, ed);
+ // NOTE(cpq): in the check sequence below is important.
+ // strncmp() must go first: it fails fast if the remaining length of
+ // the path is smaller than `n`.
+ if (depth == ed && path[pos - 1] == '.' &&
+ strncmp(&s[i + 1], &path[pos], (size_t) n) == 0 &&
+ (path[pos + n] == '\0' || path[pos + n] == '.' ||
+ path[pos + n] == '[')) {
+ pos += n;
+ }
+ i += n + 1;
+ expecting = S_COLON;
+ } else if (c == '}') { // Empty object
+ MG_EOO('}');
+ expecting = S_COMMA_OR_EOO;
+ } else {
+ return MG_JSON_INVALID;
+ }
+ break;
+
+ case S_COLON:
+ if (c == ':') {
+ expecting = S_VALUE;
+ } else {
+ return MG_JSON_INVALID;
+ }
+ break;
+
+ case S_COMMA_OR_EOO:
+ if (depth <= 0) {
+ return MG_JSON_INVALID;
+ } else if (c == ',') {
+ expecting = (nesting[depth - 1] == '{') ? S_KEY : S_VALUE;
+ } else if (c == ']' || c == '}') {
+ MG_EOO('O');
+ if (depth == ed && ei >= 0) ci++;
+ } else {
+ return MG_JSON_INVALID;
+ }
+ break;
+ }
+ }
+ return MG_JSON_NOT_FOUND;
+}
+
+bool mg_json_get_num(struct mg_str json, const char *path, double *v) {
+ int n, toklen, found = 0;
+ if ((n = mg_json_get(json, path, &toklen)) >= 0 &&
+ (json.ptr[n] == '-' || (json.ptr[n] >= '0' && json.ptr[n] <= '9'))) {
+ if (v != NULL) *v = mg_atod(json.ptr + n, toklen, NULL);
+ found = 1;
+ }
+ return found;
+}
+
+bool mg_json_get_bool(struct mg_str json, const char *path, bool *v) {
+ int found = 0, off = mg_json_get(json, path, NULL);
+ if (off >= 0 && (json.ptr[off] == 't' || json.ptr[off] == 'f')) {
+ if (v != NULL) *v = json.ptr[off] == 't';
+ found = 1;
+ }
+ return found;
+}
+
+static bool json_unescape(const char *s, size_t len, char *to, size_t n) {
+ size_t i, j;
+ for (i = 0, j = 0; i < len && j < n; i++, j++) {
+ if (s[i] == '\\' && i + 5 < len && s[i + 1] == 'u') {
+ // \uXXXX escape. We could process a simple one-byte chars
+ // \u00xx from the ASCII range. More complex chars would require
+ // dragging in a UTF8 library, which is too much for us
+ if (s[i + 2] != '0' || s[i + 3] != '0') return false; // Give up
+ ((unsigned char *) to)[j] = (unsigned char) mg_unhexn(s + i + 4, 2);
+
+ i += 5;
+ } else if (s[i] == '\\' && i + 1 < len) {
+ char c = json_esc(s[i + 1], 0);
+ if (c == 0) return false;
+ to[j] = c;
+ i++;
+ } else {
+ to[j] = s[i];
+ }
+ }
+ if (j >= n) return false;
+ if (n > 0) to[j] = '\0';
+ return true;
+}
+
+char *mg_json_get_str(struct mg_str json, const char *path) {
+ char *result = NULL;
+ int len = 0, off = mg_json_get(json, path, &len);
+ if (off >= 0 && len > 1 && json.ptr[off] == '"') {
+ if ((result = (char *) calloc(1, (size_t) len)) != NULL &&
+ !json_unescape(json.ptr + off + 1, (size_t) (len - 2), result,
+ (size_t) len)) {
+ free(result);
+ result = NULL;
+ }
+ }
+ return result;
+}
+
+char *mg_json_get_b64(struct mg_str json, const char *path, int *slen) {
+ char *result = NULL;
+ int len = 0, off = mg_json_get(json, path, &len);
+ if (off >= 0 && json.ptr[off] == '"' && len > 1 &&
+ (result = (char *) calloc(1, (size_t) len)) != NULL) {
+ int k = mg_base64_decode(json.ptr + off + 1, len - 2, result);
+ if (slen != NULL) *slen = k;
+ }
+ return result;
+}
+
+char *mg_json_get_hex(struct mg_str json, const char *path, int *slen) {
+ char *result = NULL;
+ int len = 0, off = mg_json_get(json, path, &len);
+ if (off >= 0 && json.ptr[off] == '"' && len > 1 &&
+ (result = (char *) calloc(1, (size_t) len / 2)) != NULL) {
+ mg_unhex(json.ptr + off + 1, (size_t) (len - 2), (uint8_t *) result);
+ result[len / 2 - 1] = '\0';
+ if (slen != NULL) *slen = len / 2 - 1;
+ }
+ return result;
+}
+
+long mg_json_get_long(struct mg_str json, const char *path, long dflt) {
+ double dv;
+ long result = dflt;
+ if (mg_json_get_num(json, path, &dv)) result = (long) dv;
+ return result;
+}
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/log.c"
+#endif
+
+
+
+
+
+static int s_level = MG_LL_INFO;
+static mg_pfn_t s_log_func = mg_pfn_stdout;
+static void *s_log_func_param = NULL;
+
+void mg_log_set_fn(mg_pfn_t fn, void *param) {
+ s_log_func = fn;
+ s_log_func_param = param;
+}
+
+static void logc(unsigned char c) {
+ s_log_func((char) c, s_log_func_param);
+}
+
+static void logs(const char *buf, size_t len) {
+ size_t i;
+ for (i = 0; i < len; i++) logc(((unsigned char *) buf)[i]);
+}
+
+void mg_log_set(int log_level) {
+ MG_DEBUG(("Setting log level to %d", log_level));
+ s_level = log_level;
+}
+
+bool mg_log_prefix(int level, const char *file, int line, const char *fname) {
+ if (level <= s_level) {
+ const char *p = strrchr(file, '/');
+ char buf[41];
+ size_t n;
+ if (p == NULL) p = strrchr(file, '\\');
+ n = mg_snprintf(buf, sizeof(buf), "%-6llx %d %s:%d:%s", mg_millis(), level,
+ p == NULL ? file : p + 1, line, fname);
+ if (n > sizeof(buf) - 2) n = sizeof(buf) - 2;
+ while (n < sizeof(buf)) buf[n++] = ' ';
+ logs(buf, n - 1);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void mg_log(const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ mg_vxprintf(s_log_func, s_log_func_param, fmt, &ap);
+ va_end(ap);
+ logc((unsigned char) '\n');
+}
+
+static unsigned char nibble(unsigned c) {
+ return (unsigned char) (c < 10 ? c + '0' : c + 'W');
+}
+
+#define ISPRINT(x) ((x) >= ' ' && (x) <= '~')
+void mg_hexdump(const void *buf, size_t len) {
+ const unsigned char *p = (const unsigned char *) buf;
+ unsigned char ascii[16], alen = 0;
+ size_t i;
+ for (i = 0; i < len; i++) {
+ if ((i % 16) == 0) {
+ // Print buffered ascii chars
+ if (i > 0) logs(" ", 2), logs((char *) ascii, 16), logc('\n'), alen = 0;
+ // Print hex address, then \t
+ logc(nibble((i >> 12) & 15)), logc(nibble((i >> 8) & 15)),
+ logc(nibble((i >> 4) & 15)), logc('0'), logs(" ", 3);
+ }
+ logc(nibble(p[i] >> 4)), logc(nibble(p[i] & 15)); // Two nibbles, e.g. c5
+ logc(' '); // Space after hex number
+ ascii[alen++] = ISPRINT(p[i]) ? p[i] : '.'; // Add to the ascii buf
+ }
+ while (alen < 16) logs(" ", 3), ascii[alen++] = ' ';
+ logs(" ", 2), logs((char *) ascii, 16), logc('\n');
+}
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/md5.c"
+#endif
+
+
+
+#if defined(MG_ENABLE_MD5) && MG_ENABLE_MD5
+
+static void mg_byte_reverse(unsigned char *buf, unsigned longs) {
+ if (MG_BIG_ENDIAN) {
+ do {
+ uint32_t t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+ ((unsigned) buf[1] << 8 | buf[0]);
+ *(uint32_t *) buf = t;
+ buf += 4;
+ } while (--longs);
+ } else {
+ (void) buf, (void) longs; // Little endian. Do nothing
+ }
+}
+
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+#define MD5STEP(f, w, x, y, z, data, s) \
+ (w += f(x, y, z) + data, w = w << s | w >> (32 - s), w += x)
+
+/*
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void mg_md5_init(mg_md5_ctx *ctx) {
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+}
+
+static void mg_md5_transform(uint32_t buf[4], uint32_t const in[16]) {
+ uint32_t a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+void mg_md5_update(mg_md5_ctx *ctx, const unsigned char *buf, size_t len) {
+ uint32_t t;
+
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t) ctx->bits[1]++;
+ ctx->bits[1] += (uint32_t) len >> 29;
+
+ t = (t >> 3) & 0x3f;
+
+ if (t) {
+ unsigned char *p = (unsigned char *) ctx->in + t;
+
+ t = 64 - t;
+ if (len < t) {
+ memcpy(p, buf, len);
+ return;
+ }
+ memcpy(p, buf, t);
+ mg_byte_reverse(ctx->in, 16);
+ mg_md5_transform(ctx->buf, (uint32_t *) ctx->in);
+ buf += t;
+ len -= t;
+ }
+
+ while (len >= 64) {
+ memcpy(ctx->in, buf, 64);
+ mg_byte_reverse(ctx->in, 16);
+ mg_md5_transform(ctx->buf, (uint32_t *) ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ memcpy(ctx->in, buf, len);
+}
+
+void mg_md5_final(mg_md5_ctx *ctx, unsigned char digest[16]) {
+ unsigned count;
+ unsigned char *p;
+ uint32_t *a;
+
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ p = ctx->in + count;
+ *p++ = 0x80;
+ count = 64 - 1 - count;
+ if (count < 8) {
+ memset(p, 0, count);
+ mg_byte_reverse(ctx->in, 16);
+ mg_md5_transform(ctx->buf, (uint32_t *) ctx->in);
+ memset(ctx->in, 0, 56);
+ } else {
+ memset(p, 0, count - 8);
+ }
+ mg_byte_reverse(ctx->in, 14);
+
+ a = (uint32_t *) ctx->in;
+ a[14] = ctx->bits[0];
+ a[15] = ctx->bits[1];
+
+ mg_md5_transform(ctx->buf, (uint32_t *) ctx->in);
+ mg_byte_reverse((unsigned char *) ctx->buf, 4);
+ memcpy(digest, ctx->buf, 16);
+ memset((char *) ctx, 0, sizeof(*ctx));
+}
+#endif
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/mqtt.c"
+#endif
+
+
+
+
+
+
+
+
+#define MQTT_CLEAN_SESSION 0x02
+#define MQTT_HAS_WILL 0x04
+#define MQTT_WILL_RETAIN 0x20
+#define MQTT_HAS_PASSWORD 0x40
+#define MQTT_HAS_USER_NAME 0x80
+
+struct mg_mqtt_pmap {
+ uint8_t id;
+ uint8_t type;
+};
+
+static const struct mg_mqtt_pmap s_prop_map[] = {
+ {MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, MQTT_PROP_TYPE_BYTE},
+ {MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, MQTT_PROP_TYPE_INT},
+ {MQTT_PROP_CONTENT_TYPE, MQTT_PROP_TYPE_STRING},
+ {MQTT_PROP_RESPONSE_TOPIC, MQTT_PROP_TYPE_STRING},
+ {MQTT_PROP_CORRELATION_DATA, MQTT_PROP_TYPE_BINARY_DATA},
+ {MQTT_PROP_SUBSCRIPTION_IDENTIFIER, MQTT_PROP_TYPE_VARIABLE_INT},
+ {MQTT_PROP_SESSION_EXPIRY_INTERVAL, MQTT_PROP_TYPE_INT},
+ {MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, MQTT_PROP_TYPE_STRING},
+ {MQTT_PROP_SERVER_KEEP_ALIVE, MQTT_PROP_TYPE_SHORT},
+ {MQTT_PROP_AUTHENTICATION_METHOD, MQTT_PROP_TYPE_STRING},
+ {MQTT_PROP_AUTHENTICATION_DATA, MQTT_PROP_TYPE_BINARY_DATA},
+ {MQTT_PROP_REQUEST_PROBLEM_INFORMATION, MQTT_PROP_TYPE_BYTE},
+ {MQTT_PROP_WILL_DELAY_INTERVAL, MQTT_PROP_TYPE_INT},
+ {MQTT_PROP_REQUEST_RESPONSE_INFORMATION, MQTT_PROP_TYPE_BYTE},
+ {MQTT_PROP_RESPONSE_INFORMATION, MQTT_PROP_TYPE_STRING},
+ {MQTT_PROP_SERVER_REFERENCE, MQTT_PROP_TYPE_STRING},
+ {MQTT_PROP_REASON_STRING, MQTT_PROP_TYPE_STRING},
+ {MQTT_PROP_RECEIVE_MAXIMUM, MQTT_PROP_TYPE_SHORT},
+ {MQTT_PROP_TOPIC_ALIAS_MAXIMUM, MQTT_PROP_TYPE_SHORT},
+ {MQTT_PROP_TOPIC_ALIAS, MQTT_PROP_TYPE_SHORT},
+ {MQTT_PROP_MAXIMUM_QOS, MQTT_PROP_TYPE_BYTE},
+ {MQTT_PROP_RETAIN_AVAILABLE, MQTT_PROP_TYPE_BYTE},
+ {MQTT_PROP_USER_PROPERTY, MQTT_PROP_TYPE_STRING_PAIR},
+ {MQTT_PROP_MAXIMUM_PACKET_SIZE, MQTT_PROP_TYPE_INT},
+ {MQTT_PROP_WILDCARD_SUBSCRIPTION_AVAILABLE, MQTT_PROP_TYPE_BYTE},
+ {MQTT_PROP_SUBSCRIPTION_IDENTIFIER_AVAILABLE, MQTT_PROP_TYPE_BYTE},
+ {MQTT_PROP_SHARED_SUBSCRIPTION_AVAILABLE, MQTT_PROP_TYPE_BYTE}};
+
+void mg_mqtt_send_header(struct mg_connection *c, uint8_t cmd, uint8_t flags,
+ uint32_t len) {
+ uint8_t buf[1 + sizeof(len)], *vlen = &buf[1];
+ buf[0] = (uint8_t) ((cmd << 4) | flags);
+ do {
+ *vlen = len % 0x80;
+ len /= 0x80;
+ if (len > 0) *vlen |= 0x80;
+ vlen++;
+ } while (len > 0 && vlen < &buf[sizeof(buf)]);
+ mg_send(c, buf, (size_t) (vlen - buf));
+}
+
+static void mg_send_u16(struct mg_connection *c, uint16_t value) {
+ mg_send(c, &value, sizeof(value));
+}
+
+static void mg_send_u32(struct mg_connection *c, uint32_t value) {
+ mg_send(c, &value, sizeof(value));
+}
+
+static uint8_t compute_variable_length_size(size_t length) {
+ uint8_t bytes_needed = 0;
+ do {
+ bytes_needed++;
+ length /= 0x80;
+ } while (length > 0);
+ return bytes_needed;
+}
+
+static int encode_variable_length(uint8_t *buf, size_t value) {
+ int len = 0;
+
+ do {
+ uint8_t byte = (uint8_t) (value % 128);
+ value /= 128;
+ if (value > 0) byte |= 0x80;
+ buf[len++] = byte;
+ } while (value > 0);
+
+ return len;
+}
+
+static uint32_t decode_variable_length(const char *buf,
+ uint32_t *bytes_consumed) {
+ uint32_t value = 0, multiplier = 1, offset;
+
+ for (offset = 0; offset < 4; offset++) {
+ uint8_t encoded_byte = ((uint8_t *) buf)[offset];
+ value += (encoded_byte & 0x7F) * multiplier;
+ multiplier *= 128;
+
+ if (!(encoded_byte & 0x80)) break;
+ }
+
+ if (bytes_consumed != NULL) *bytes_consumed = offset + 1;
+
+ return value;
+}
+
+static int mqtt_prop_type_by_id(uint8_t prop_id) {
+ size_t i, num_properties = sizeof(s_prop_map) / sizeof(s_prop_map[0]);
+ for (i = 0; i < num_properties; ++i) {
+ if (s_prop_map[i].id == prop_id) return s_prop_map[i].type;
+ }
+ return -1; // Property ID not found
+}
+
+// Returns the size of the properties section, without the
+// size of the content's length
+static size_t get_properties_length(struct mg_mqtt_prop *props, size_t count) {
+ size_t i, size = 0;
+ for (i = 0; i < count; i++) {
+ size++; // identifier
+ switch (mqtt_prop_type_by_id(props[i].id)) {
+ case MQTT_PROP_TYPE_STRING_PAIR:
+ size += (uint32_t) (props[i].val.len + props[i].key.len +
+ 2 * sizeof(uint16_t));
+ break;
+ case MQTT_PROP_TYPE_STRING:
+ size += (uint32_t) (props[i].val.len + sizeof(uint16_t));
+ break;
+ case MQTT_PROP_TYPE_BINARY_DATA:
+ size += (uint32_t) (props[i].val.len + sizeof(uint16_t));
+ break;
+ case MQTT_PROP_TYPE_VARIABLE_INT:
+ size += compute_variable_length_size((uint32_t) props[i].iv);
+ break;
+ case MQTT_PROP_TYPE_INT: size += (uint32_t) sizeof(uint32_t); break;
+ case MQTT_PROP_TYPE_SHORT: size += (uint32_t) sizeof(uint16_t); break;
+ default: return size; // cannot parse further down
+ }
+ }
+
+ return size;
+}
+
+// returns the entire size of the properties section, including the
+// size of the variable length of the content
+static size_t get_props_size(struct mg_mqtt_prop *props, size_t count) {
+ size_t size = get_properties_length(props, count);
+ size += compute_variable_length_size(size);
+ return size;
+}
+
+static void mg_send_mqtt_properties(struct mg_connection *c,
+ struct mg_mqtt_prop *props, size_t nprops) {
+ size_t total_size = get_properties_length(props, nprops);
+ uint8_t buf_v[4] = {0, 0, 0, 0};
+ uint8_t buf[4] = {0, 0, 0, 0};
+ int i, len = encode_variable_length(buf, total_size);
+
+ mg_send(c, buf, (size_t) len);
+ for (i = 0; i < (int) nprops; i++) {
+ mg_send(c, &props[i].id, sizeof(props[i].id));
+ switch (mqtt_prop_type_by_id(props[i].id)) {
+ case MQTT_PROP_TYPE_STRING_PAIR:
+ mg_send_u16(c, mg_htons((uint16_t) props[i].key.len));
+ mg_send(c, props[i].key.ptr, props[i].key.len);
+ mg_send_u16(c, mg_htons((uint16_t) props[i].val.len));
+ mg_send(c, props[i].val.ptr, props[i].val.len);
+ break;
+ case MQTT_PROP_TYPE_BYTE:
+ mg_send(c, &props[i].iv, sizeof(uint8_t));
+ break;
+ case MQTT_PROP_TYPE_SHORT:
+ mg_send_u16(c, mg_htons((uint16_t) props[i].iv));
+ break;
+ case MQTT_PROP_TYPE_INT:
+ mg_send_u32(c, mg_htonl((uint32_t) props[i].iv));
+ break;
+ case MQTT_PROP_TYPE_STRING:
+ mg_send_u16(c, mg_htons((uint16_t) props[i].val.len));
+ mg_send(c, props[i].val.ptr, props[i].val.len);
+ break;
+ case MQTT_PROP_TYPE_BINARY_DATA:
+ mg_send_u16(c, mg_htons((uint16_t) props[i].val.len));
+ mg_send(c, props[i].val.ptr, props[i].val.len);
+ break;
+ case MQTT_PROP_TYPE_VARIABLE_INT:
+ len = encode_variable_length(buf_v, props[i].iv);
+ mg_send(c, buf_v, (size_t) len);
+ break;
+ }
+ }
+}
+
+size_t mg_mqtt_next_prop(struct mg_mqtt_message *msg, struct mg_mqtt_prop *prop,
+ size_t ofs) {
+ uint8_t *i = (uint8_t *) msg->dgram.ptr + msg->props_start + ofs;
+ size_t new_pos = ofs;
+ uint32_t bytes_consumed;
+ prop->id = i[0];
+
+ if (ofs >= msg->dgram.len || ofs >= msg->props_start + msg->props_size)
+ return 0;
+ i++, new_pos++;
+
+ switch (mqtt_prop_type_by_id(prop->id)) {
+ case MQTT_PROP_TYPE_STRING_PAIR:
+ prop->key.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]);
+ prop->key.ptr = (char *) i + 2;
+ i += 2 + prop->key.len;
+ prop->val.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]);
+ prop->val.ptr = (char *) i + 2;
+ new_pos += 2 * sizeof(uint16_t) + prop->val.len + prop->key.len;
+ break;
+ case MQTT_PROP_TYPE_BYTE:
+ prop->iv = (uint8_t) i[0];
+ new_pos++;
+ break;
+ case MQTT_PROP_TYPE_SHORT:
+ prop->iv = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]);
+ new_pos += sizeof(uint16_t);
+ break;
+ case MQTT_PROP_TYPE_INT:
+ prop->iv = ((uint32_t) i[0] << 24) | ((uint32_t) i[1] << 16) |
+ ((uint32_t) i[2] << 8) | i[3];
+ new_pos += sizeof(uint32_t);
+ break;
+ case MQTT_PROP_TYPE_STRING:
+ prop->val.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]);
+ prop->val.ptr = (char *) i + 2;
+ new_pos += 2 + prop->val.len;
+ break;
+ case MQTT_PROP_TYPE_BINARY_DATA:
+ prop->val.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]);
+ prop->val.ptr = (char *) i + 2;
+ new_pos += 2 + prop->val.len;
+ break;
+ case MQTT_PROP_TYPE_VARIABLE_INT:
+ prop->iv = decode_variable_length((char *) i, &bytes_consumed);
+ new_pos += bytes_consumed;
+ break;
+ default: new_pos = 0;
+ }
+
+ return new_pos;
+}
+
+void mg_mqtt_login(struct mg_connection *c, const struct mg_mqtt_opts *opts) {
+ char rnd[10], client_id[21];
+ struct mg_str cid = opts->client_id;
+ size_t total_len = 7 + 1 + 2 + 2;
+ uint8_t hdr[8] = {0, 4, 'M', 'Q', 'T', 'T', opts->version, 0};
+
+ if (cid.len == 0) {
+ mg_random(rnd, sizeof(rnd));
+ mg_hex(rnd, sizeof(rnd), client_id);
+ client_id[sizeof(client_id) - 1] = '\0';
+ cid = mg_str(client_id);
+ }
+
+ if (hdr[6] == 0) hdr[6] = 4; // If version is not set, use 4 (3.1.1)
+ c->is_mqtt5 = hdr[6] == 5; // Set version 5 flag
+ hdr[7] = (uint8_t) ((opts->qos & 3) << 3); // Connection flags
+ if (opts->user.len > 0) {
+ total_len += 2 + (uint32_t) opts->user.len;
+ hdr[7] |= MQTT_HAS_USER_NAME;
+ }
+ if (opts->pass.len > 0) {
+ total_len += 2 + (uint32_t) opts->pass.len;
+ hdr[7] |= MQTT_HAS_PASSWORD;
+ }
+ if (opts->topic.len > 0 && opts->message.len > 0) {
+ total_len += 4 + (uint32_t) opts->topic.len + (uint32_t) opts->message.len;
+ hdr[7] |= MQTT_HAS_WILL;
+ }
+ if (opts->clean || cid.len == 0) hdr[7] |= MQTT_CLEAN_SESSION;
+ if (opts->retain) hdr[7] |= MQTT_WILL_RETAIN;
+ total_len += (uint32_t) cid.len;
+ if (c->is_mqtt5) {
+ total_len += get_props_size(opts->props, opts->num_props);
+ if (hdr[7] & MQTT_HAS_WILL)
+ total_len += get_props_size(opts->will_props, opts->num_will_props);
+ }
+
+ mg_mqtt_send_header(c, MQTT_CMD_CONNECT, 0, (uint32_t) total_len);
+ mg_send(c, hdr, sizeof(hdr));
+ // keepalive == 0 means "do not disconnect us!"
+ mg_send_u16(c, mg_htons((uint16_t) opts->keepalive));
+
+ if (c->is_mqtt5) mg_send_mqtt_properties(c, opts->props, opts->num_props);
+
+ mg_send_u16(c, mg_htons((uint16_t) cid.len));
+ mg_send(c, cid.ptr, cid.len);
+
+ if (hdr[7] & MQTT_HAS_WILL) {
+ if (c->is_mqtt5)
+ mg_send_mqtt_properties(c, opts->will_props, opts->num_will_props);
+
+ mg_send_u16(c, mg_htons((uint16_t) opts->topic.len));
+ mg_send(c, opts->topic.ptr, opts->topic.len);
+ mg_send_u16(c, mg_htons((uint16_t) opts->message.len));
+ mg_send(c, opts->message.ptr, opts->message.len);
+ }
+ if (opts->user.len > 0) {
+ mg_send_u16(c, mg_htons((uint16_t) opts->user.len));
+ mg_send(c, opts->user.ptr, opts->user.len);
+ }
+ if (opts->pass.len > 0) {
+ mg_send_u16(c, mg_htons((uint16_t) opts->pass.len));
+ mg_send(c, opts->pass.ptr, opts->pass.len);
+ }
+}
+
+void mg_mqtt_pub(struct mg_connection *c, const struct mg_mqtt_opts *opts) {
+ uint8_t flags = (uint8_t) (((opts->qos & 3) << 1) | (opts->retain ? 1 : 0));
+ size_t len = 2 + opts->topic.len + opts->message.len;
+ MG_DEBUG(("%lu [%.*s] -> [%.*s]", c->id, (int) opts->topic.len,
+ (char *) opts->topic.ptr, (int) opts->message.len,
+ (char *) opts->message.ptr));
+ if (opts->qos > 0) len += 2;
+ if (c->is_mqtt5) len += get_props_size(opts->props, opts->num_props);
+
+ mg_mqtt_send_header(c, MQTT_CMD_PUBLISH, flags, (uint32_t) len);
+ mg_send_u16(c, mg_htons((uint16_t) opts->topic.len));
+ mg_send(c, opts->topic.ptr, opts->topic.len);
+ if (opts->qos > 0) {
+ if (++c->mgr->mqtt_id == 0) ++c->mgr->mqtt_id;
+ mg_send_u16(c, mg_htons(c->mgr->mqtt_id));
+ }
+
+ if (c->is_mqtt5) mg_send_mqtt_properties(c, opts->props, opts->num_props);
+
+ mg_send(c, opts->message.ptr, opts->message.len);
+}
+
+void mg_mqtt_sub(struct mg_connection *c, const struct mg_mqtt_opts *opts) {
+ uint8_t qos_ = opts->qos & 3;
+ size_t plen = c->is_mqtt5 ? get_props_size(opts->props, opts->num_props) : 0;
+ size_t len = 2 + opts->topic.len + 2 + 1 + plen;
+
+ mg_mqtt_send_header(c, MQTT_CMD_SUBSCRIBE, 2, (uint32_t) len);
+ if (++c->mgr->mqtt_id == 0) ++c->mgr->mqtt_id;
+ mg_send_u16(c, mg_htons(c->mgr->mqtt_id));
+ if (c->is_mqtt5) mg_send_mqtt_properties(c, opts->props, opts->num_props);
+
+ mg_send_u16(c, mg_htons((uint16_t) opts->topic.len));
+ mg_send(c, opts->topic.ptr, opts->topic.len);
+ mg_send(c, &qos_, sizeof(qos_));
+}
+
+int mg_mqtt_parse(const uint8_t *buf, size_t len, uint8_t version,
+ struct mg_mqtt_message *m) {
+ uint8_t lc = 0, *p, *end;
+ uint32_t n = 0, len_len = 0;
+
+ memset(m, 0, sizeof(*m));
+ m->dgram.ptr = (char *) buf;
+ if (len < 2) return MQTT_INCOMPLETE;
+ m->cmd = (uint8_t) (buf[0] >> 4);
+ m->qos = (buf[0] >> 1) & 3;
+
+ n = len_len = 0;
+ p = (uint8_t *) buf + 1;
+ while ((size_t) (p - buf) < len) {
+ lc = *((uint8_t *) p++);
+ n += (uint32_t) ((lc & 0x7f) << 7 * len_len);
+ len_len++;
+ if (!(lc & 0x80)) break;
+ if (len_len >= 4) return MQTT_MALFORMED;
+ }
+ end = p + n;
+ if ((lc & 0x80) || (end > buf + len)) return MQTT_INCOMPLETE;
+ m->dgram.len = (size_t) (end - buf);
+
+ switch (m->cmd) {
+ case MQTT_CMD_CONNACK:
+ if (end - p < 2) return MQTT_MALFORMED;
+ m->ack = p[1];
+ break;
+ case MQTT_CMD_PUBACK:
+ case MQTT_CMD_PUBREC:
+ case MQTT_CMD_PUBREL:
+ case MQTT_CMD_PUBCOMP:
+ case MQTT_CMD_SUBSCRIBE:
+ case MQTT_CMD_SUBACK:
+ case MQTT_CMD_UNSUBSCRIBE:
+ case MQTT_CMD_UNSUBACK:
+ if (p + 2 > end) return MQTT_MALFORMED;
+ m->id = (uint16_t) ((((uint16_t) p[0]) << 8) | p[1]);
+ p += 2;
+ break;
+ case MQTT_CMD_PUBLISH: {
+ if (p + 2 > end) return MQTT_MALFORMED;
+ m->topic.len = (uint16_t) ((((uint16_t) p[0]) << 8) | p[1]);
+ m->topic.ptr = (char *) p + 2;
+ p += 2 + m->topic.len;
+ if (p > end) return MQTT_MALFORMED;
+ if (m->qos > 0) {
+ if (p + 2 > end) return MQTT_MALFORMED;
+ m->id = (uint16_t) ((((uint16_t) p[0]) << 8) | p[1]);
+ p += 2;
+ }
+ if (p > end) return MQTT_MALFORMED;
+ if (version == 5 && p + 2 < end) {
+ m->props_size = decode_variable_length((char *) p, &len_len);
+ m->props_start = (size_t) (p + len_len - buf);
+ p += len_len + m->props_size;
+ }
+ if (p > end) return MQTT_MALFORMED;
+ m->data.ptr = (char *) p;
+ m->data.len = (size_t) (end - p);
+ break;
+ }
+ default: break;
+ }
+ return MQTT_OK;
+}
+
+static void mqtt_cb(struct mg_connection *c, int ev, void *ev_data,
+ void *fn_data) {
+ if (ev == MG_EV_READ) {
+ for (;;) {
+ uint8_t version = c->is_mqtt5 ? 5 : 4;
+ struct mg_mqtt_message mm;
+ int rc = mg_mqtt_parse(c->recv.buf, c->recv.len, version, &mm);
+ if (rc == MQTT_MALFORMED) {
+ MG_ERROR(("%lu MQTT malformed message", c->id));
+ c->is_closing = 1;
+ break;
+ } else if (rc == MQTT_OK) {
+ MG_VERBOSE(("%lu MQTT CMD %d len %d [%.*s]", c->id, mm.cmd,
+ (int) mm.dgram.len, (int) mm.data.len, mm.data.ptr));
+ switch (mm.cmd) {
+ case MQTT_CMD_CONNACK:
+ mg_call(c, MG_EV_MQTT_OPEN, &mm.ack);
+ if (mm.ack == 0) {
+ MG_DEBUG(("%lu Connected", c->id));
+ } else {
+ MG_ERROR(("%lu MQTT auth failed, code %d", c->id, mm.ack));
+ c->is_closing = 1;
+ }
+ break;
+ case MQTT_CMD_PUBLISH: {
+ MG_DEBUG(("%lu [%.*s] -> [%.*s]", c->id, (int) mm.topic.len,
+ mm.topic.ptr, (int) mm.data.len, mm.data.ptr));
+ if (mm.qos > 0) {
+ uint16_t id = mg_htons(mm.id);
+ uint32_t remaining_len = sizeof(id);
+ if (c->is_mqtt5) remaining_len += 1;
+
+ mg_mqtt_send_header(c, MQTT_CMD_PUBACK, 0, remaining_len);
+ mg_send(c, &id, sizeof(id));
+
+ if (c->is_mqtt5) {
+ uint16_t zero = 0;
+ mg_send(c, &zero, sizeof(zero));
+ }
+ }
+ mg_call(c, MG_EV_MQTT_MSG, &mm);
+ break;
+ }
+ }
+ mg_call(c, MG_EV_MQTT_CMD, &mm);
+ mg_iobuf_del(&c->recv, 0, mm.dgram.len);
+ } else {
+ break;
+ }
+ }
+ }
+ (void) ev_data;
+ (void) fn_data;
+}
+
+void mg_mqtt_ping(struct mg_connection *nc) {
+ mg_mqtt_send_header(nc, MQTT_CMD_PINGREQ, 0, 0);
+}
+
+void mg_mqtt_pong(struct mg_connection *nc) {
+ mg_mqtt_send_header(nc, MQTT_CMD_PINGRESP, 0, 0);
+}
+
+void mg_mqtt_disconnect(struct mg_connection *c,
+ const struct mg_mqtt_opts *opts) {
+ size_t len = 0;
+ if (c->is_mqtt5) len = 1 + get_props_size(opts->props, opts->num_props);
+ mg_mqtt_send_header(c, MQTT_CMD_DISCONNECT, 0, (uint32_t) len);
+
+ if (c->is_mqtt5) {
+ uint8_t zero = 0;
+ mg_send(c, &zero, sizeof(zero)); // reason code
+ mg_send_mqtt_properties(c, opts->props, opts->num_props);
+ }
+}
+
+struct mg_connection *mg_mqtt_connect(struct mg_mgr *mgr, const char *url,
+ const struct mg_mqtt_opts *opts,
+ mg_event_handler_t fn, void *fn_data) {
+ struct mg_connection *c = mg_connect(mgr, url, fn, fn_data);
+ if (c != NULL) {
+ struct mg_mqtt_opts empty;
+ memset(&empty, 0, sizeof(empty));
+ mg_mqtt_login(c, opts == NULL ? &empty : opts);
+ c->pfn = mqtt_cb;
+ }
+ return c;
+}
+
+struct mg_connection *mg_mqtt_listen(struct mg_mgr *mgr, const char *url,
+ mg_event_handler_t fn, void *fn_data) {
+ struct mg_connection *c = mg_listen(mgr, url, fn, fn_data);
+ if (c != NULL) c->pfn = mqtt_cb, c->pfn_data = mgr;
+ return c;
+}
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/net.c"
+#endif
+
+
+
+
+
+
+
+
+size_t mg_vprintf(struct mg_connection *c, const char *fmt, va_list *ap) {
+ size_t old = c->send.len;
+ mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, ap);
+ return c->send.len - old;
+}
+
+size_t mg_printf(struct mg_connection *c, const char *fmt, ...) {
+ size_t len = 0;
+ va_list ap;
+ va_start(ap, fmt);
+ len = mg_vprintf(c, fmt, &ap);
+ va_end(ap);
+ return len;
+}
+
+static bool mg_atonl(struct mg_str str, struct mg_addr *addr) {
+ if (mg_vcasecmp(&str, "localhost") != 0) return false;
+ addr->ip = mg_htonl(0x7f000001);
+ addr->is_ip6 = false;
+ return true;
+}
+
+static bool mg_atone(struct mg_str str, struct mg_addr *addr) {
+ if (str.len > 0) return false;
+ addr->ip = 0;
+ addr->is_ip6 = false;
+ return true;
+}
+
+static bool mg_aton4(struct mg_str str, struct mg_addr *addr) {
+ uint8_t data[4] = {0, 0, 0, 0};
+ size_t i, num_dots = 0;
+ for (i = 0; i < str.len; i++) {
+ if (str.ptr[i] >= '0' && str.ptr[i] <= '9') {
+ int octet = data[num_dots] * 10 + (str.ptr[i] - '0');
+ if (octet > 255) return false;
+ data[num_dots] = (uint8_t) octet;
+ } else if (str.ptr[i] == '.') {
+ if (num_dots >= 3 || i == 0 || str.ptr[i - 1] == '.') return false;
+ num_dots++;
+ } else {
+ return false;
+ }
+ }
+ if (num_dots != 3 || str.ptr[i - 1] == '.') return false;
+ memcpy(&addr->ip, data, sizeof(data));
+ addr->is_ip6 = false;
+ return true;
+}
+
+static bool mg_v4mapped(struct mg_str str, struct mg_addr *addr) {
+ int i;
+ if (str.len < 14) return false;
+ if (str.ptr[0] != ':' || str.ptr[1] != ':' || str.ptr[6] != ':') return false;
+ for (i = 2; i < 6; i++) {
+ if (str.ptr[i] != 'f' && str.ptr[i] != 'F') return false;
+ }
+ if (!mg_aton4(mg_str_n(&str.ptr[7], str.len - 7), addr)) return false;
+ memset(addr->ip6, 0, sizeof(addr->ip6));
+ addr->ip6[10] = addr->ip6[11] = 255;
+ memcpy(&addr->ip6[12], &addr->ip, 4);
+ addr->is_ip6 = true;
+ return true;
+}
+
+static bool mg_aton6(struct mg_str str, struct mg_addr *addr) {
+ size_t i, j = 0, n = 0, dc = 42;
+ if (str.len > 2 && str.ptr[0] == '[') str.ptr++, str.len -= 2;
+ if (mg_v4mapped(str, addr)) return true;
+ for (i = 0; i < str.len; i++) {
+ if ((str.ptr[i] >= '0' && str.ptr[i] <= '9') ||
+ (str.ptr[i] >= 'a' && str.ptr[i] <= 'f') ||
+ (str.ptr[i] >= 'A' && str.ptr[i] <= 'F')) {
+ unsigned long val;
+ if (i > j + 3) return false;
+ // MG_DEBUG(("%zu %zu [%.*s]", i, j, (int) (i - j + 1), &str.ptr[j]));
+ val = mg_unhexn(&str.ptr[j], i - j + 1);
+ addr->ip6[n] = (uint8_t) ((val >> 8) & 255);
+ addr->ip6[n + 1] = (uint8_t) (val & 255);
+ } else if (str.ptr[i] == ':') {
+ j = i + 1;
+ if (i > 0 && str.ptr[i - 1] == ':') {
+ dc = n; // Double colon
+ if (i > 1 && str.ptr[i - 2] == ':') return false;
+ } else if (i > 0) {
+ n += 2;
+ }
+ if (n > 14) return false;
+ addr->ip6[n] = addr->ip6[n + 1] = 0; // For trailing ::
+ } else {
+ return false;
+ }
+ }
+ if (n < 14 && dc == 42) return false;
+ if (n < 14) {
+ memmove(&addr->ip6[dc + (14 - n)], &addr->ip6[dc], n - dc + 2);
+ memset(&addr->ip6[dc], 0, 14 - n);
+ }
+ addr->is_ip6 = true;
+ return true;
+}
+
+bool mg_aton(struct mg_str str, struct mg_addr *addr) {
+ // MG_INFO(("[%.*s]", (int) str.len, str.ptr));
+ return mg_atone(str, addr) || mg_atonl(str, addr) || mg_aton4(str, addr) ||
+ mg_aton6(str, addr);
+}
+
+struct mg_connection *mg_alloc_conn(struct mg_mgr *mgr) {
+ struct mg_connection *c =
+ (struct mg_connection *) calloc(1, sizeof(*c) + mgr->extraconnsize);
+ if (c != NULL) {
+ c->mgr = mgr;
+ c->send.align = c->recv.align = MG_IO_SIZE;
+ c->id = ++mgr->nextid;
+ }
+ return c;
+}
+
+void mg_close_conn(struct mg_connection *c) {
+ mg_resolve_cancel(c); // Close any pending DNS query
+ LIST_DELETE(struct mg_connection, &c->mgr->conns, c);
+ if (c == c->mgr->dns4.c) c->mgr->dns4.c = NULL;
+ if (c == c->mgr->dns6.c) c->mgr->dns6.c = NULL;
+ // Order of operations is important. `MG_EV_CLOSE` event must be fired
+ // before we deallocate received data, see #1331
+ mg_call(c, MG_EV_CLOSE, NULL);
+ MG_DEBUG(("%lu %p closed", c->id, c->fd));
+
+ mg_tls_free(c);
+ mg_iobuf_free(&c->recv);
+ mg_iobuf_free(&c->send);
+ memset(c, 0, sizeof(*c));
+ free(c);
+}
+
+struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *url,
+ mg_event_handler_t fn, void *fn_data) {
+ struct mg_connection *c = NULL;
+ if (url == NULL || url[0] == '\0') {
+ MG_ERROR(("null url"));
+ } else if ((c = mg_alloc_conn(mgr)) == NULL) {
+ MG_ERROR(("OOM"));
+ } else {
+ LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c);
+ c->is_udp = (strncmp(url, "udp:", 4) == 0);
+ c->fd = (void *) (size_t) MG_INVALID_SOCKET;
+ c->fn = fn;
+ c->is_client = true;
+ c->fn_data = fn_data;
+ MG_DEBUG(("%lu %p %s", c->id, c->fd, url));
+ mg_call(c, MG_EV_OPEN, NULL);
+ mg_resolve(c, url);
+ }
+ return c;
+}
+
+struct mg_connection *mg_listen(struct mg_mgr *mgr, const char *url,
+ mg_event_handler_t fn, void *fn_data) {
+ struct mg_connection *c = NULL;
+ if ((c = mg_alloc_conn(mgr)) == NULL) {
+ MG_ERROR(("OOM %s", url));
+ } else if (!mg_open_listener(c, url)) {
+ MG_ERROR(("Failed: %s, errno %d", url, errno));
+ free(c);
+ c = NULL;
+ } else {
+ c->is_listening = 1;
+ c->is_udp = strncmp(url, "udp:", 4) == 0;
+ LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c);
+ c->fn = fn;
+ c->fn_data = fn_data;
+ mg_call(c, MG_EV_OPEN, NULL);
+ MG_DEBUG(("%lu %p %s", c->id, c->fd, url));
+ }
+ return c;
+}
+
+struct mg_connection *mg_wrapfd(struct mg_mgr *mgr, int fd,
+ mg_event_handler_t fn, void *fn_data) {
+ struct mg_connection *c = mg_alloc_conn(mgr);
+ if (c != NULL) {
+ c->fd = (void *) (size_t) fd;
+ c->fn = fn;
+ c->fn_data = fn_data;
+ MG_EPOLL_ADD(c);
+ mg_call(c, MG_EV_OPEN, NULL);
+ LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c);
+ }
+ return c;
+}
+
+struct mg_timer *mg_timer_add(struct mg_mgr *mgr, uint64_t milliseconds,
+ unsigned flags, void (*fn)(void *), void *arg) {
+ struct mg_timer *t = (struct mg_timer *) calloc(1, sizeof(*t));
+ if (t != NULL) {
+ mg_timer_init(&mgr->timers, t, milliseconds, flags, fn, arg);
+ t->id = mgr->timerid++;
+ }
+ return t;
+}
+
+void mg_mgr_free(struct mg_mgr *mgr) {
+ struct mg_connection *c;
+ struct mg_timer *tmp, *t = mgr->timers;
+ while (t != NULL) tmp = t->next, free(t), t = tmp;
+ mgr->timers = NULL; // Important. Next call to poll won't touch timers
+ for (c = mgr->conns; c != NULL; c = c->next) c->is_closing = 1;
+ mg_mgr_poll(mgr, 0);
+#if MG_ENABLE_FREERTOS_TCP
+ FreeRTOS_DeleteSocketSet(mgr->ss);
+#endif
+ MG_DEBUG(("All connections closed"));
+#if MG_ENABLE_EPOLL
+ if (mgr->epoll_fd >= 0) close(mgr->epoll_fd), mgr->epoll_fd = -1;
+#endif
+}
+
+void mg_mgr_init(struct mg_mgr *mgr) {
+ memset(mgr, 0, sizeof(*mgr));
+#if MG_ENABLE_EPOLL
+ if ((mgr->epoll_fd = epoll_create1(0)) < 0) MG_ERROR(("epoll: %d", errno));
+#else
+ mgr->epoll_fd = -1;
+#endif
+#if MG_ARCH == MG_ARCH_WIN32 && MG_ENABLE_WINSOCK
+ // clang-format off
+ { WSADATA data; WSAStartup(MAKEWORD(2, 2), &data); }
+ // clang-format on
+#elif MG_ENABLE_FREERTOS_TCP
+ mgr->ss = FreeRTOS_CreateSocketSet();
+#elif defined(__unix) || defined(__unix__) || defined(__APPLE__)
+ // Ignore SIGPIPE signal, so if client cancels the request, it
+ // won't kill the whole process.
+ signal(SIGPIPE, SIG_IGN);
+#endif
+ mgr->dnstimeout = 3000;
+ mgr->dns4.url = "udp://8.8.8.8:53";
+ mgr->dns6.url = "udp://[2001:4860:4860::8888]:53";
+}
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/printf.c"
+#endif
+
+
+
+
+size_t mg_queue_vprintf(struct mg_queue *q, const char *fmt, va_list *ap) {
+ size_t len = mg_snprintf(NULL, 0, fmt, ap);
+ char *buf;
+ if (len == 0 || mg_queue_book(q, &buf, len + 1) < len + 1) {
+ len = 0; // Nah. Not enough space
+ } else {
+ len = mg_vsnprintf((char *) buf, len + 1, fmt, ap);
+ mg_queue_add(q, len);
+ }
+ return len;
+}
+
+size_t mg_queue_printf(struct mg_queue *q, const char *fmt, ...) {
+ va_list ap;
+ size_t len;
+ va_start(ap, fmt);
+ len = mg_queue_vprintf(q, fmt, &ap);
+ va_end(ap);
+ return len;
+}
+
+static void mg_pfn_iobuf_private(char ch, void *param, bool expand) {
+ struct mg_iobuf *io = (struct mg_iobuf *) param;
+ if (expand && io->len + 2 > io->size) mg_iobuf_resize(io, io->len + 2);
+ if (io->len + 2 <= io->size) {
+ io->buf[io->len++] = (uint8_t) ch;
+ io->buf[io->len] = 0;
+ } else if (io->len < io->size) {
+ io->buf[io->len++] = 0; // Guarantee to 0-terminate
+ }
+}
+
+static void mg_putchar_iobuf_static(char ch, void *param) {
+ mg_pfn_iobuf_private(ch, param, false);
+}
+
+void mg_pfn_iobuf(char ch, void *param) {
+ mg_pfn_iobuf_private(ch, param, true);
+}
+
+size_t mg_vsnprintf(char *buf, size_t len, const char *fmt, va_list *ap) {
+ struct mg_iobuf io = {(uint8_t *) buf, len, 0, 0};
+ size_t n = mg_vxprintf(mg_putchar_iobuf_static, &io, fmt, ap);
+ if (n < len) buf[n] = '\0';
+ return n;
+}
+
+size_t mg_snprintf(char *buf, size_t len, const char *fmt, ...) {
+ va_list ap;
+ size_t n;
+ va_start(ap, fmt);
+ n = mg_vsnprintf(buf, len, fmt, &ap);
+ va_end(ap);
+ return n;
+}
+
+char *mg_vmprintf(const char *fmt, va_list *ap) {
+ struct mg_iobuf io = {0, 0, 0, 256};
+ mg_vxprintf(mg_pfn_iobuf, &io, fmt, ap);
+ return (char *) io.buf;
+}
+
+char *mg_mprintf(const char *fmt, ...) {
+ char *s;
+ va_list ap;
+ va_start(ap, fmt);
+ s = mg_vmprintf(fmt, &ap);
+ va_end(ap);
+ return s;
+}
+
+void mg_pfn_stdout(char c, void *param) {
+ putchar(c);
+ (void) param;
+}
+
+static size_t print_ip4(void (*out)(char, void *), void *arg, uint8_t *p) {
+ return mg_xprintf(out, arg, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
+}
+
+static size_t print_ip6(void (*out)(char, void *), void *arg, uint16_t *p) {
+ return mg_xprintf(out, arg, "[%x:%x:%x:%x:%x:%x:%x:%x]", mg_ntohs(p[0]),
+ mg_ntohs(p[1]), mg_ntohs(p[2]), mg_ntohs(p[3]),
+ mg_ntohs(p[4]), mg_ntohs(p[5]), mg_ntohs(p[6]),
+ mg_ntohs(p[7]));
+}
+
+size_t mg_print_ip4(void (*out)(char, void *), void *arg, va_list *ap) {
+ uint8_t *p = va_arg(*ap, uint8_t *);
+ return print_ip4(out, arg, p);
+}
+
+size_t mg_print_ip6(void (*out)(char, void *), void *arg, va_list *ap) {
+ uint16_t *p = va_arg(*ap, uint16_t *);
+ return print_ip6(out, arg, p);
+}
+
+size_t mg_print_ip(void (*out)(char, void *), void *arg, va_list *ap) {
+ struct mg_addr *addr = va_arg(*ap, struct mg_addr *);
+ if (addr->is_ip6) return print_ip6(out, arg, (uint16_t *) addr->ip6);
+ return print_ip4(out, arg, (uint8_t *) &addr->ip);
+}
+
+size_t mg_print_ip_port(void (*out)(char, void *), void *arg, va_list *ap) {
+ struct mg_addr *a = va_arg(*ap, struct mg_addr *);
+ return mg_xprintf(out, arg, "%M:%hu", mg_print_ip, a, mg_ntohs(a->port));
+}
+
+size_t mg_print_mac(void (*out)(char, void *), void *arg, va_list *ap) {
+ uint8_t *p = va_arg(*ap, uint8_t *);
+ return mg_xprintf(out, arg, "%02x:%02x:%02x:%02x:%02x:%02x", p[0], p[1], p[2],
+ p[3], p[4], p[5]);
+}
+
+static char mg_esc(int c, bool esc) {
+ const char *p, *esc1 = "\b\f\n\r\t\\\"", *esc2 = "bfnrt\\\"";
+ for (p = esc ? esc1 : esc2; *p != '\0'; p++) {
+ if (*p == c) return esc ? esc2[p - esc1] : esc1[p - esc2];
+ }
+ return 0;
+}
+
+static char mg_escape(int c) {
+ return mg_esc(c, true);
+}
+
+static size_t qcpy(void (*out)(char, void *), void *ptr, char *buf,
+ size_t len) {
+ size_t i = 0, extra = 0;
+ for (i = 0; i < len && buf[i] != '\0'; i++) {
+ char c = mg_escape(buf[i]);
+ if (c) {
+ out('\\', ptr), out(c, ptr), extra++;
+ } else {
+ out(buf[i], ptr);
+ }
+ }
+ return i + extra;
+}
+
+static size_t bcpy(void (*out)(char, void *), void *arg, uint8_t *buf,
+ size_t len) {
+ size_t i, j, n = 0;
+ const char *t =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ for (i = 0; i < len; i += 3) {
+ uint8_t c1 = buf[i], c2 = i + 1 < len ? buf[i + 1] : 0,
+ c3 = i + 2 < len ? buf[i + 2] : 0;
+ char tmp[4] = {t[c1 >> 2], t[(c1 & 3) << 4 | (c2 >> 4)], '=', '='};
+ if (i + 1 < len) tmp[2] = t[(c2 & 15) << 2 | (c3 >> 6)];
+ if (i + 2 < len) tmp[3] = t[c3 & 63];
+ for (j = 0; j < sizeof(tmp) && tmp[j] != '\0'; j++) out(tmp[j], arg);
+ n += j;
+ }
+ return n;
+}
+
+size_t mg_print_hex(void (*out)(char, void *), void *arg, va_list *ap) {
+ size_t bl = (size_t) va_arg(*ap, int);
+ uint8_t *p = va_arg(*ap, uint8_t *);
+ const char *hex = "0123456789abcdef";
+ size_t j;
+ for (j = 0; j < bl; j++) {
+ out(hex[(p[j] >> 4) & 0x0F], arg);
+ out(hex[p[j] & 0x0F], arg);
+ }
+ return 2 * bl;
+}
+size_t mg_print_base64(void (*out)(char, void *), void *arg, va_list *ap) {
+ size_t len = (size_t) va_arg(*ap, int);
+ uint8_t *buf = va_arg(*ap, uint8_t *);
+ return bcpy(out, arg, buf, len);
+}
+
+size_t mg_print_esc(void (*out)(char, void *), void *arg, va_list *ap) {
+ size_t len = (size_t) va_arg(*ap, int);
+ char *p = va_arg(*ap, char *);
+ if (len == 0) len = p == NULL ? 0 : strlen(p);
+ return qcpy(out, arg, p, len);
+}
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/queue.c"
+#endif
+
+
+
+#if defined(__GNUC__) || defined(__clang__)
+#define MG_MEMORY_BARRIER() __sync_synchronize()
+#elif defined(_MSC_VER) && _MSC_VER >= 1700
+#define MG_MEMORY_BARRIER() MemoryBarrier()
+#elif !defined(MG_MEMORY_BARRIER)
+#define MG_MEMORY_BARRIER()
+#endif
+
+// Every message in a queue is prepended by a 32-bit message length (ML).
+// If ML is 0, then it is the end, and reader must wrap to the beginning.
+//
+// Queue when q->tail <= q->head:
+// |----- free -----| ML | message1 | ML | message2 | ----- free ------|
+// ^ ^ ^ ^
+// buf tail head len
+//
+// Queue when q->tail > q->head:
+// | ML | message2 |----- free ------| ML | message1 | 0 |---- free ----|
+// ^ ^ ^ ^
+// buf head tail len
+
+void mg_queue_init(struct mg_queue *q, char *buf, size_t size) {
+ q->size = size;
+ q->buf = buf;
+ q->head = q->tail = 0;
+}
+
+static size_t mg_queue_read_len(struct mg_queue *q) {
+ uint32_t n = 0;
+ MG_MEMORY_BARRIER();
+ memcpy(&n, q->buf + q->tail, sizeof(n));
+ assert(q->tail + n + sizeof(n) <= q->size);
+ return n;
+}
+
+static void mg_queue_write_len(struct mg_queue *q, size_t len) {
+ uint32_t n = (uint32_t) len;
+ memcpy(q->buf + q->head, &n, sizeof(n));
+ MG_MEMORY_BARRIER();
+}
+
+size_t mg_queue_book(struct mg_queue *q, char **buf, size_t len) {
+ size_t space = 0, hs = sizeof(uint32_t) * 2; // *2 is for the 0 marker
+ if (q->head >= q->tail && q->head + len + hs <= q->size) {
+ space = q->size - q->head - hs; // There is enough space
+ } else if (q->head >= q->tail && q->tail > hs) {
+ mg_queue_write_len(q, 0); // Not enough space ahead
+ q->head = 0; // Wrap head to the beginning
+ }
+ if (q->head + hs + len < q->tail) space = q->tail - q->head - hs;
+ if (buf != NULL) *buf = q->buf + q->head + sizeof(uint32_t);
+ return space;
+}
+
+size_t mg_queue_next(struct mg_queue *q, char **buf) {
+ size_t len = 0;
+ if (q->tail != q->head) {
+ len = mg_queue_read_len(q);
+ if (len == 0) { // Zero (head wrapped) ?
+ q->tail = 0; // Reset tail to the start
+ if (q->head > q->tail) len = mg_queue_read_len(q); // Read again
+ }
+ }
+ if (buf != NULL) *buf = q->buf + q->tail + sizeof(uint32_t);
+ assert(q->tail + len <= q->size);
+ return len;
+}
+
+void mg_queue_add(struct mg_queue *q, size_t len) {
+ assert(len > 0);
+ mg_queue_write_len(q, len);
+ assert(q->head + sizeof(uint32_t) * 2 + len <= q->size);
+ q->head += len + sizeof(uint32_t);
+}
+
+void mg_queue_del(struct mg_queue *q, size_t len) {
+ q->tail += len + sizeof(uint32_t);
+ assert(q->tail + sizeof(uint32_t) <= q->size);
+}
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/rpc.c"
+#endif
+
+
+
+void mg_rpc_add(struct mg_rpc **head, struct mg_str method,
+ void (*fn)(struct mg_rpc_req *), void *fn_data) {
+ struct mg_rpc *rpc = (struct mg_rpc *) calloc(1, sizeof(*rpc));
+ if (rpc != NULL) {
+ rpc->method = mg_strdup(method), rpc->fn = fn, rpc->fn_data = fn_data;
+ rpc->next = *head, *head = rpc;
+ }
+}
+
+void mg_rpc_del(struct mg_rpc **head, void (*fn)(struct mg_rpc_req *)) {
+ struct mg_rpc *r;
+ while ((r = *head) != NULL) {
+ if (r->fn == fn || fn == NULL) {
+ *head = r->next;
+ free((void *) r->method.ptr);
+ free(r);
+ } else {
+ head = &(*head)->next;
+ }
+ }
+}
+
+static void mg_rpc_call(struct mg_rpc_req *r, struct mg_str method) {
+ struct mg_rpc *h = r->head == NULL ? NULL : *r->head;
+ while (h != NULL && !mg_match(method, h->method, NULL)) h = h->next;
+ if (h != NULL) {
+ r->rpc = h;
+ h->fn(r);
+ } else {
+ mg_rpc_err(r, -32601, "\"%.*s not found\"", (int) method.len, method.ptr);
+ }
+}
+
+void mg_rpc_process(struct mg_rpc_req *r) {
+ int len, off = mg_json_get(r->frame, "$.method", &len);
+ if (off > 0 && r->frame.ptr[off] == '"') {
+ struct mg_str method = mg_str_n(&r->frame.ptr[off + 1], (size_t) len - 2);
+ mg_rpc_call(r, method);
+ } else if ((off = mg_json_get(r->frame, "$.result", &len)) > 0 ||
+ (off = mg_json_get(r->frame, "$.error", &len)) > 0) {
+ mg_rpc_call(r, mg_str("")); // JSON response! call "" method handler
+ } else {
+ mg_rpc_err(r, -32700, "%m", mg_print_esc, (int) r->frame.len,
+ r->frame.ptr); // Invalid
+ }
+}
+
+void mg_rpc_vok(struct mg_rpc_req *r, const char *fmt, va_list *ap) {
+ int len, off = mg_json_get(r->frame, "$.id", &len);
+ if (off > 0) {
+ mg_xprintf(r->pfn, r->pfn_data, "{%m:%.*s,%m:", mg_print_esc, 0, "id", len,
+ &r->frame.ptr[off], mg_print_esc, 0, "result");
+ mg_vxprintf(r->pfn, r->pfn_data, fmt == NULL ? "null" : fmt, ap);
+ mg_xprintf(r->pfn, r->pfn_data, "}");
+ }
+}
+
+void mg_rpc_ok(struct mg_rpc_req *r, const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ mg_rpc_vok(r, fmt, &ap);
+ va_end(ap);
+}
+
+void mg_rpc_verr(struct mg_rpc_req *r, int code, const char *fmt, va_list *ap) {
+ int len, off = mg_json_get(r->frame, "$.id", &len);
+ mg_xprintf(r->pfn, r->pfn_data, "{");
+ if (off > 0) {
+ mg_xprintf(r->pfn, r->pfn_data, "%m:%.*s,", mg_print_esc, 0, "id", len,
+ &r->frame.ptr[off]);
+ }
+ mg_xprintf(r->pfn, r->pfn_data, "%m:{%m:%d,%m:", mg_print_esc, 0, "error",
+ mg_print_esc, 0, "code", code, mg_print_esc, 0, "message");
+ mg_vxprintf(r->pfn, r->pfn_data, fmt == NULL ? "null" : fmt, ap);
+ mg_xprintf(r->pfn, r->pfn_data, "}}");
+}
+
+void mg_rpc_err(struct mg_rpc_req *r, int code, const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ mg_rpc_verr(r, code, fmt, &ap);
+ va_end(ap);
+}
+
+static size_t print_methods(mg_pfn_t pfn, void *pfn_data, va_list *ap) {
+ struct mg_rpc *h, **head = (struct mg_rpc **) va_arg(*ap, void **);
+ size_t len = 0;
+ for (h = *head; h != NULL; h = h->next) {
+ if (h->method.len == 0) continue; // Ignore response handler
+ len += mg_xprintf(pfn, pfn_data, "%s%m", h == *head ? "" : ",",
+ mg_print_esc, (int) h->method.len, h->method.ptr);
+ }
+ return len;
+}
+
+void mg_rpc_list(struct mg_rpc_req *r) {
+ mg_rpc_ok(r, "[%M]", print_methods, r->head);
+}
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/sha1.c"
+#endif
+/* Copyright(c) By Steve Reid <steve@edmweb.com> */
+/* 100% Public Domain */
+
+
+
+union char64long16 {
+ unsigned char c[64];
+ uint32_t l[16];
+};
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+static uint32_t blk0(union char64long16 *block, int i) {
+ if (MG_BIG_ENDIAN) {
+ } else {
+ block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) |
+ (rol(block->l[i], 8) & 0x00FF00FF);
+ }
+ return block->l[i];
+}
+
+/* Avoid redefine warning (ARM /usr/include/sys/ucontext.h define R0~R4) */
+#undef blk
+#undef R0
+#undef R1
+#undef R2
+#undef R3
+#undef R4
+
+#define blk(i) \
+ (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ block->l[(i + 8) & 15] ^ \
+ block->l[(i + 2) & 15] ^ block->l[i & 15], \
+ 1))
+#define R0(v, w, x, y, z, i) \
+ z += ((w & (x ^ y)) ^ y) + blk0(block, i) + 0x5A827999 + rol(v, 5); \
+ w = rol(w, 30);
+#define R1(v, w, x, y, z, i) \
+ z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \
+ w = rol(w, 30);
+#define R2(v, w, x, y, z, i) \
+ z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); \
+ w = rol(w, 30);
+#define R3(v, w, x, y, z, i) \
+ z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \
+ w = rol(w, 30);
+#define R4(v, w, x, y, z, i) \
+ z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \
+ w = rol(w, 30);
+
+static void mg_sha1_transform(uint32_t state[5],
+ const unsigned char *buffer) {
+ uint32_t a, b, c, d, e;
+ union char64long16 block[1];
+
+ memcpy(block, buffer, 64);
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+ e = state[4];
+ R0(a, b, c, d, e, 0);
+ R0(e, a, b, c, d, 1);
+ R0(d, e, a, b, c, 2);
+ R0(c, d, e, a, b, 3);
+ R0(b, c, d, e, a, 4);
+ R0(a, b, c, d, e, 5);
+ R0(e, a, b, c, d, 6);
+ R0(d, e, a, b, c, 7);
+ R0(c, d, e, a, b, 8);
+ R0(b, c, d, e, a, 9);
+ R0(a, b, c, d, e, 10);
+ R0(e, a, b, c, d, 11);
+ R0(d, e, a, b, c, 12);
+ R0(c, d, e, a, b, 13);
+ R0(b, c, d, e, a, 14);
+ R0(a, b, c, d, e, 15);
+ R1(e, a, b, c, d, 16);
+ R1(d, e, a, b, c, 17);
+ R1(c, d, e, a, b, 18);
+ R1(b, c, d, e, a, 19);
+ R2(a, b, c, d, e, 20);
+ R2(e, a, b, c, d, 21);
+ R2(d, e, a, b, c, 22);
+ R2(c, d, e, a, b, 23);
+ R2(b, c, d, e, a, 24);
+ R2(a, b, c, d, e, 25);
+ R2(e, a, b, c, d, 26);
+ R2(d, e, a, b, c, 27);
+ R2(c, d, e, a, b, 28);
+ R2(b, c, d, e, a, 29);
+ R2(a, b, c, d, e, 30);
+ R2(e, a, b, c, d, 31);
+ R2(d, e, a, b, c, 32);
+ R2(c, d, e, a, b, 33);
+ R2(b, c, d, e, a, 34);
+ R2(a, b, c, d, e, 35);
+ R2(e, a, b, c, d, 36);
+ R2(d, e, a, b, c, 37);
+ R2(c, d, e, a, b, 38);
+ R2(b, c, d, e, a, 39);
+ R3(a, b, c, d, e, 40);
+ R3(e, a, b, c, d, 41);
+ R3(d, e, a, b, c, 42);
+ R3(c, d, e, a, b, 43);
+ R3(b, c, d, e, a, 44);
+ R3(a, b, c, d, e, 45);
+ R3(e, a, b, c, d, 46);
+ R3(d, e, a, b, c, 47);
+ R3(c, d, e, a, b, 48);
+ R3(b, c, d, e, a, 49);
+ R3(a, b, c, d, e, 50);
+ R3(e, a, b, c, d, 51);
+ R3(d, e, a, b, c, 52);
+ R3(c, d, e, a, b, 53);
+ R3(b, c, d, e, a, 54);
+ R3(a, b, c, d, e, 55);
+ R3(e, a, b, c, d, 56);
+ R3(d, e, a, b, c, 57);
+ R3(c, d, e, a, b, 58);
+ R3(b, c, d, e, a, 59);
+ R4(a, b, c, d, e, 60);
+ R4(e, a, b, c, d, 61);
+ R4(d, e, a, b, c, 62);
+ R4(c, d, e, a, b, 63);
+ R4(b, c, d, e, a, 64);
+ R4(a, b, c, d, e, 65);
+ R4(e, a, b, c, d, 66);
+ R4(d, e, a, b, c, 67);
+ R4(c, d, e, a, b, 68);
+ R4(b, c, d, e, a, 69);
+ R4(a, b, c, d, e, 70);
+ R4(e, a, b, c, d, 71);
+ R4(d, e, a, b, c, 72);
+ R4(c, d, e, a, b, 73);
+ R4(b, c, d, e, a, 74);
+ R4(a, b, c, d, e, 75);
+ R4(e, a, b, c, d, 76);
+ R4(d, e, a, b, c, 77);
+ R4(c, d, e, a, b, 78);
+ R4(b, c, d, e, a, 79);
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+ /* Erase working structures. The order of operations is important,
+ * used to ensure that compiler doesn't optimize those out. */
+ memset(block, 0, sizeof(block));
+ a = b = c = d = e = 0;
+ (void) a;
+ (void) b;
+ (void) c;
+ (void) d;
+ (void) e;
+}
+
+void mg_sha1_init(mg_sha1_ctx *context) {
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xEFCDAB89;
+ context->state[2] = 0x98BADCFE;
+ context->state[3] = 0x10325476;
+ context->state[4] = 0xC3D2E1F0;
+ context->count[0] = context->count[1] = 0;
+}
+
+void mg_sha1_update(mg_sha1_ctx *context, const unsigned char *data,
+ size_t len) {
+ size_t i, j;
+
+ j = context->count[0];
+ if ((context->count[0] += (uint32_t) len << 3) < j) context->count[1]++;
+ context->count[1] += (uint32_t) (len >> 29);
+ j = (j >> 3) & 63;
+ if ((j + len) > 63) {
+ memcpy(&context->buffer[j], data, (i = 64 - j));
+ mg_sha1_transform(context->state, context->buffer);
+ for (; i + 63 < len; i += 64) {
+ mg_sha1_transform(context->state, &data[i]);
+ }
+ j = 0;
+ } else
+ i = 0;
+ memcpy(&context->buffer[j], &data[i], len - i);
+}
+
+void mg_sha1_final(unsigned char digest[20], mg_sha1_ctx *context) {
+ unsigned i;
+ unsigned char finalcount[8], c;
+
+ for (i = 0; i < 8; i++) {
+ finalcount[i] = (unsigned char) ((context->count[(i >= 4 ? 0 : 1)] >>
+ ((3 - (i & 3)) * 8)) &
+ 255);
+ }
+ c = 0200;
+ mg_sha1_update(context, &c, 1);
+ while ((context->count[0] & 504) != 448) {
+ c = 0000;
+ mg_sha1_update(context, &c, 1);
+ }
+ mg_sha1_update(context, finalcount, 8);
+ for (i = 0; i < 20; i++) {
+ digest[i] =
+ (unsigned char) ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255);
+ }
+ memset(context, '\0', sizeof(*context));
+ memset(&finalcount, '\0', sizeof(finalcount));
+}
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/sntp.c"
+#endif
+
+
+
+
+
+
+#define SNTP_TIME_OFFSET 2208988800U // (1970 - 1900) in seconds
+#define SNTP_MAX_FRAC 4294967295.0 // 2 ** 32 - 1
+
+static int64_t gettimestamp(const uint32_t *data) {
+ uint32_t sec = mg_ntohl(data[0]), frac = mg_ntohl(data[1]);
+ if (sec) sec -= SNTP_TIME_OFFSET;
+ return ((int64_t) sec) * 1000 + (int64_t) (frac / SNTP_MAX_FRAC * 1000.0);
+}
+
+int64_t mg_sntp_parse(const unsigned char *buf, size_t len) {
+ int64_t res = -1;
+ int mode = len > 0 ? buf[0] & 7 : 0;
+ int version = len > 0 ? (buf[0] >> 3) & 7 : 0;
+ if (len < 48) {
+ MG_ERROR(("%s", "corrupt packet"));
+ } else if (mode != 4 && mode != 5) {
+ MG_ERROR(("%s", "not a server reply"));
+ } else if (buf[1] == 0) {
+ MG_ERROR(("%s", "server sent a kiss of death"));
+ } else if (version == 4 || version == 3) {
+ // int64_t ref = gettimestamp((uint32_t *) &buf[16]);
+ int64_t t0 = gettimestamp((uint32_t *) &buf[24]);
+ int64_t t1 = gettimestamp((uint32_t *) &buf[32]);
+ int64_t t2 = gettimestamp((uint32_t *) &buf[40]);
+ int64_t t3 = (int64_t) mg_millis();
+ int64_t delta = (t3 - t0) - (t2 - t1);
+ MG_VERBOSE(("%lld %lld %lld %lld delta:%lld", t0, t1, t2, t3, delta));
+ res = t2 + delta / 2;
+ } else {
+ MG_ERROR(("unexpected version: %d", version));
+ }
+ return res;
+}
+
+static void sntp_cb(struct mg_connection *c, int ev, void *evd, void *fnd) {
+ if (ev == MG_EV_READ) {
+ int64_t milliseconds = mg_sntp_parse(c->recv.buf, c->recv.len);
+ if (milliseconds > 0) {
+ MG_INFO(("%lu got time: %lld ms from epoch", c->id, milliseconds));
+ mg_call(c, MG_EV_SNTP_TIME, (uint64_t *) &milliseconds);
+ MG_VERBOSE(("%u.%u", (unsigned) (milliseconds / 1000),
+ (unsigned) (milliseconds % 1000)));
+ }
+ mg_iobuf_del(&c->recv, 0, c->recv.len); // Free receive buffer
+ } else if (ev == MG_EV_CONNECT) {
+ mg_sntp_request(c);
+ } else if (ev == MG_EV_CLOSE) {
+ }
+ (void) fnd;
+ (void) evd;
+}
+
+void mg_sntp_request(struct mg_connection *c) {
+ if (c->is_resolving) {
+ MG_ERROR(("%lu wait until resolved", c->id));
+ } else {
+ int64_t now = (int64_t) mg_millis(); // Use int64_t, for vc98
+ uint8_t buf[48] = {0};
+ uint32_t *t = (uint32_t *) &buf[40];
+ double frac = ((double) (now % 1000)) / 1000.0 * SNTP_MAX_FRAC;
+ buf[0] = (0 << 6) | (4 << 3) | 3;
+ t[0] = mg_htonl((uint32_t) (now / 1000) + SNTP_TIME_OFFSET);
+ t[1] = mg_htonl((uint32_t) frac);
+ mg_send(c, buf, sizeof(buf));
+ }
+}
+
+struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr, const char *url,
+ mg_event_handler_t fn, void *fnd) {
+ struct mg_connection *c = NULL;
+ if (url == NULL) url = "udp://time.google.com:123";
+ if ((c = mg_connect(mgr, url, fn, fnd)) != NULL) c->pfn = sntp_cb;
+ return c;
+}
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/sock.c"
+#endif
+
+
+
+
+
+
+
+
+
+
+
+#if MG_ENABLE_SOCKET
+
+#ifndef closesocket
+#define closesocket(x) close(x)
+#endif
+
+#define FD(c_) ((MG_SOCKET_TYPE) (size_t) (c_)->fd)
+#define S2PTR(s_) ((void *) (size_t) (s_))
+
+#ifndef MSG_NONBLOCKING
+#define MSG_NONBLOCKING 0
+#endif
+
+#ifndef AF_INET6
+#define AF_INET6 10
+#endif
+
+#ifndef MG_SOCK_ERR
+#define MG_SOCK_ERR(errcode) ((errcode) < 0 ? errno : 0)
+#endif
+
+#ifndef MG_SOCK_INTR
+#define MG_SOCK_INTR(fd) (fd == MG_INVALID_SOCKET && MG_SOCK_ERR(-1) == EINTR)
+#endif
+
+#ifndef MG_SOCK_PENDING
+#define MG_SOCK_PENDING(errcode) \
+ (((errcode) < 0) && (errno == EINPROGRESS || errno == EWOULDBLOCK))
+#endif
+
+#ifndef MG_SOCK_RESET
+#define MG_SOCK_RESET(errcode) \
+ (((errcode) < 0) && (errno == EPIPE || errno == ECONNRESET))
+#endif
+
+union usa {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+#if MG_ENABLE_IPV6
+ struct sockaddr_in6 sin6;
+#endif
+};
+
+static socklen_t tousa(struct mg_addr *a, union usa *usa) {
+ socklen_t len = sizeof(usa->sin);
+ memset(usa, 0, sizeof(*usa));
+ usa->sin.sin_family = AF_INET;
+ usa->sin.sin_port = a->port;
+ *(uint32_t *) &usa->sin.sin_addr = a->ip;
+#if MG_ENABLE_IPV6
+ if (a->is_ip6) {
+ usa->sin.sin_family = AF_INET6;
+ usa->sin6.sin6_port = a->port;
+ memcpy(&usa->sin6.sin6_addr, a->ip6, sizeof(a->ip6));
+ len = sizeof(usa->sin6);
+ }
+#endif
+ return len;
+}
+
+static void tomgaddr(union usa *usa, struct mg_addr *a, bool is_ip6) {
+ a->is_ip6 = is_ip6;
+ a->port = usa->sin.sin_port;
+ memcpy(&a->ip, &usa->sin.sin_addr, sizeof(a->ip));
+#if MG_ENABLE_IPV6
+ if (is_ip6) {
+ memcpy(a->ip6, &usa->sin6.sin6_addr, sizeof(a->ip6));
+ a->port = usa->sin6.sin6_port;
+ }
+#endif
+}
+
+static void setlocaddr(MG_SOCKET_TYPE fd, struct mg_addr *addr) {
+ union usa usa;
+ socklen_t n = sizeof(usa);
+ if (getsockname(fd, &usa.sa, &n) == 0) {
+ tomgaddr(&usa, addr, n != sizeof(usa.sin));
+ }
+}
+
+static void iolog(struct mg_connection *c, char *buf, long n, bool r) {
+ if (n == MG_IO_WAIT) {
+ // Do nothing
+ } else if (n <= 0) {
+ c->is_closing = 1; // Termination. Don't call mg_error(): #1529
+ } else if (n > 0) {
+ if (c->is_hexdumping) {
+ union usa usa;
+ socklen_t slen = sizeof(usa.sin);
+ if (getsockname(FD(c), &usa.sa, &slen) < 0) (void) 0; // Ignore result
+ MG_INFO(("\n-- %lu %M %s %M %ld", c->id, mg_print_ip_port, &c->loc,
+ r ? "<-" : "->", mg_print_ip_port, &c->rem, n));
+
+ mg_hexdump(buf, (size_t) n);
+ }
+ if (r) {
+ c->recv.len += (size_t) n;
+ mg_call(c, MG_EV_READ, &n);
+ } else {
+ mg_iobuf_del(&c->send, 0, (size_t) n);
+ // if (c->send.len == 0) mg_iobuf_resize(&c->send, 0);
+ if (c->send.len == 0) {
+ MG_EPOLL_MOD(c, 0);
+ }
+ mg_call(c, MG_EV_WRITE, &n);
+ }
+ }
+}
+
+long mg_io_send(struct mg_connection *c, const void *buf, size_t len) {
+ long n;
+ if (c->is_udp) {
+ union usa usa;
+ socklen_t slen = tousa(&c->rem, &usa);
+ n = sendto(FD(c), (char *) buf, len, 0, &usa.sa, slen);
+ if (n > 0) setlocaddr(FD(c), &c->loc);
+ } else {
+ n = send(FD(c), (char *) buf, len, MSG_NONBLOCKING);
+ }
+ if (MG_SOCK_PENDING(n)) return MG_IO_WAIT;
+ if (MG_SOCK_RESET(n)) return MG_IO_RESET;
+ if (n <= 0) return MG_IO_ERR;
+ return n;
+}
+
+bool mg_send(struct mg_connection *c, const void *buf, size_t len) {
+ if (c->is_udp) {
+ long n = mg_io_send(c, buf, len);
+ MG_DEBUG(("%lu %p %d:%d %ld err %d", c->id, c->fd, (int) c->send.len,
+ (int) c->recv.len, n, MG_SOCK_ERR(n)));
+ iolog(c, (char *) buf, n, false);
+ return n > 0;
+ } else {
+ return mg_iobuf_add(&c->send, c->send.len, buf, len);
+ }
+}
+
+static void mg_set_non_blocking_mode(MG_SOCKET_TYPE fd) {
+#if defined(MG_CUSTOM_NONBLOCK)
+ MG_CUSTOM_NONBLOCK(fd);
+#elif MG_ARCH == MG_ARCH_WIN32 && MG_ENABLE_WINSOCK
+ unsigned long on = 1;
+ ioctlsocket(fd, FIONBIO, &on);
+#elif MG_ENABLE_RL
+ unsigned long on = 1;
+ ioctlsocket(fd, FIONBIO, &on);
+#elif MG_ENABLE_FREERTOS_TCP
+ const BaseType_t off = 0;
+ if (setsockopt(fd, 0, FREERTOS_SO_RCVTIMEO, &off, sizeof(off)) != 0) (void) 0;
+ if (setsockopt(fd, 0, FREERTOS_SO_SNDTIMEO, &off, sizeof(off)) != 0) (void) 0;
+#elif MG_ENABLE_LWIP
+ lwip_fcntl(fd, F_SETFL, O_NONBLOCK);
+#elif MG_ARCH == MG_ARCH_AZURERTOS
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+#elif MG_ARCH == MG_ARCH_TIRTOS
+ int val = 0;
+ setsockopt(fd, SOL_SOCKET, SO_BLOCKING, &val, sizeof(val));
+ // SPRU524J section 3.3.3 page 63, SO_SNDLOWAT
+ int sz = sizeof(val);
+ getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &val, &sz);
+ val /= 2; // set send low-water mark at half send buffer size
+ setsockopt(fd, SOL_SOCKET, SO_SNDLOWAT, &val, sizeof(val));
+#else
+ fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); // Non-blocking mode
+ fcntl(fd, F_SETFD, FD_CLOEXEC); // Set close-on-exec
+#endif
+}
+
+bool mg_open_listener(struct mg_connection *c, const char *url) {
+ MG_SOCKET_TYPE fd = MG_INVALID_SOCKET;
+ bool success = false;
+ c->loc.port = mg_htons(mg_url_port(url));
+ if (!mg_aton(mg_url_host(url), &c->loc)) {
+ MG_ERROR(("invalid listening URL: %s", url));
+ } else {
+ union usa usa;
+ socklen_t slen = tousa(&c->loc, &usa);
+ int rc, on = 1, af = c->loc.is_ip6 ? AF_INET6 : AF_INET;
+ int type = strncmp(url, "udp:", 4) == 0 ? SOCK_DGRAM : SOCK_STREAM;
+ int proto = type == SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP;
+ (void) on;
+
+ if ((fd = socket(af, type, proto)) == MG_INVALID_SOCKET) {
+ MG_ERROR(("socket: %d", MG_SOCK_ERR(-1)));
+#if defined(SO_EXCLUSIVEADDRUSE)
+ } else if ((rc = setsockopt(fd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
+ (char *) &on, sizeof(on))) != 0) {
+ // "Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE"
+ MG_ERROR(("setsockopt(SO_EXCLUSIVEADDRUSE): %d %d", on, MG_SOCK_ERR(rc)));
+#elif defined(SO_REUSEADDR) && (!defined(LWIP_SOCKET) || SO_REUSE)
+ } else if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on,
+ sizeof(on))) != 0) {
+ // 1. SO_REUSEADDR semantics on UNIX and Windows is different. On
+ // Windows, SO_REUSEADDR allows to bind a socket to a port without error
+ // even if the port is already open by another program. This is not the
+ // behavior SO_REUSEADDR was designed for, and leads to hard-to-track
+ // failure scenarios.
+ //
+ // 2. For LWIP, SO_REUSEADDR should be explicitly enabled by defining
+ // SO_REUSE = 1 in lwipopts.h, otherwise the code below will compile but
+ // won't work! (setsockopt will return EINVAL)
+ MG_ERROR(("setsockopt(SO_REUSEADDR): %d", MG_SOCK_ERR(rc)));
+#endif
+#if defined(IPV6_V6ONLY)
+ } else if (c->loc.is_ip6 &&
+ (rc = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &on,
+ sizeof(on))) != 0) {
+ // See #2089. Allow to bind v4 and v6 sockets on the same port
+ MG_ERROR(("setsockopt(IPV6_V6ONLY): %d", MG_SOCK_ERR(rc)));
+#endif
+ } else if ((rc = bind(fd, &usa.sa, slen)) != 0) {
+ MG_ERROR(("bind: %d", MG_SOCK_ERR(rc)));
+ } else if ((type == SOCK_STREAM &&
+ (rc = listen(fd, MG_SOCK_LISTEN_BACKLOG_SIZE)) != 0)) {
+ // NOTE(lsm): FreeRTOS uses backlog value as a connection limit
+ // In case port was set to 0, get the real port number
+ MG_ERROR(("listen: %d", MG_SOCK_ERR(rc)));
+ } else {
+ setlocaddr(fd, &c->loc);
+ mg_set_non_blocking_mode(fd);
+ c->fd = S2PTR(fd);
+ MG_EPOLL_ADD(c);
+ success = true;
+ }
+ }
+ if (success == false && fd != MG_INVALID_SOCKET) closesocket(fd);
+ return success;
+}
+
+long mg_io_recv(struct mg_connection *c, void *buf, size_t len) {
+ long n = 0;
+ if (c->is_udp) {
+ union usa usa;
+ socklen_t slen = tousa(&c->rem, &usa);
+ n = recvfrom(FD(c), (char *) buf, len, 0, &usa.sa, &slen);
+ if (n > 0) tomgaddr(&usa, &c->rem, slen != sizeof(usa.sin));
+ } else {
+ n = recv(FD(c), (char *) buf, len, MSG_NONBLOCKING);
+ }
+ if (MG_SOCK_PENDING(n)) return MG_IO_WAIT;
+ if (MG_SOCK_RESET(n)) return MG_IO_RESET;
+ if (n <= 0) return MG_IO_ERR;
+ return n;
+}
+
+// NOTE(lsm): do only one iteration of reads, cause some systems
+// (e.g. FreeRTOS stack) return 0 instead of -1/EWOULDBLOCK when no data
+static void read_conn(struct mg_connection *c) {
+ long n = -1;
+ if (c->recv.len >= MG_MAX_RECV_SIZE) {
+ mg_error(c, "max_recv_buf_size reached");
+ } else if (c->recv.size <= c->recv.len &&
+ !mg_iobuf_resize(&c->recv, c->recv.size + MG_IO_SIZE)) {
+ mg_error(c, "oom");
+ } else {
+ char *buf = (char *) &c->recv.buf[c->recv.len];
+ size_t len = c->recv.size - c->recv.len;
+ n = c->is_tls ? mg_tls_recv(c, buf, len) : mg_io_recv(c, buf, len);
+ MG_DEBUG(("%lu %p snd %ld/%ld rcv %ld/%ld n=%ld err=%d", c->id, c->fd,
+ (long) c->send.len, (long) c->send.size, (long) c->recv.len,
+ (long) c->recv.size, n, MG_SOCK_ERR(n)));
+ iolog(c, buf, n, true);
+ }
+}
+
+static void write_conn(struct mg_connection *c) {
+ char *buf = (char *) c->send.buf;
+ size_t len = c->send.len;
+ long n = c->is_tls ? mg_tls_send(c, buf, len) : mg_io_send(c, buf, len);
+ MG_DEBUG(("%lu %p snd %ld/%ld rcv %ld/%ld n=%ld err=%d", c->id, c->fd,
+ (long) c->send.len, (long) c->send.size, (long) c->recv.len,
+ (long) c->recv.size, n, MG_SOCK_ERR(n)));
+ iolog(c, buf, n, false);
+}
+
+static void close_conn(struct mg_connection *c) {
+ if (FD(c) != MG_INVALID_SOCKET) {
+#if MG_ENABLE_EPOLL
+ epoll_ctl(c->mgr->epoll_fd, EPOLL_CTL_DEL, FD(c), NULL);
+#endif
+ closesocket(FD(c));
+#if MG_ENABLE_FREERTOS_TCP
+ FreeRTOS_FD_CLR(c->fd, c->mgr->ss, eSELECT_ALL);
+#endif
+ }
+ mg_close_conn(c);
+}
+
+static void connect_conn(struct mg_connection *c) {
+ union usa usa;
+ socklen_t n = sizeof(usa);
+ // Use getpeername() to test whether we have connected
+ if (getpeername(FD(c), &usa.sa, &n) == 0) {
+ c->is_connecting = 0;
+ mg_call(c, MG_EV_CONNECT, NULL);
+ MG_EPOLL_MOD(c, 0);
+ if (c->is_tls_hs) mg_tls_handshake(c);
+ } else {
+ mg_error(c, "socket error");
+ }
+}
+
+static void setsockopts(struct mg_connection *c) {
+#if MG_ENABLE_FREERTOS_TCP || MG_ARCH == MG_ARCH_AZURERTOS || \
+ MG_ARCH == MG_ARCH_TIRTOS
+ (void) c;
+#else
+ int on = 1;
+#if !defined(SOL_TCP)
+#define SOL_TCP IPPROTO_TCP
+#endif
+ if (setsockopt(FD(c), SOL_TCP, TCP_NODELAY, (char *) &on, sizeof(on)) != 0)
+ (void) 0;
+ if (setsockopt(FD(c), SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on)) !=
+ 0)
+ (void) 0;
+#endif
+}
+
+void mg_connect_resolved(struct mg_connection *c) {
+ int type = c->is_udp ? SOCK_DGRAM : SOCK_STREAM;
+ int rc, af = c->rem.is_ip6 ? AF_INET6 : AF_INET; // c->rem has resolved IP
+ c->fd = S2PTR(socket(af, type, 0)); // Create outbound socket
+ c->is_resolving = 0; // Clear resolving flag
+ if (FD(c) == MG_INVALID_SOCKET) {
+ mg_error(c, "socket(): %d", MG_SOCK_ERR(-1));
+ } else if (c->is_udp) {
+ MG_EPOLL_ADD(c);
+#if MG_ARCH == MG_ARCH_TIRTOS
+ union usa usa; // TI-RTOS NDK requires binding to receive on UDP sockets
+ socklen_t slen = tousa(&c->loc, &usa);
+ if ((rc = bind(c->fd, &usa.sa, slen)) != 0)
+ MG_ERROR(("bind: %d", MG_SOCK_ERR(rc)));
+#endif
+ mg_call(c, MG_EV_RESOLVE, NULL);
+ mg_call(c, MG_EV_CONNECT, NULL);
+ } else {
+ union usa usa;
+ socklen_t slen = tousa(&c->rem, &usa);
+ mg_set_non_blocking_mode(FD(c));
+ setsockopts(c);
+ MG_EPOLL_ADD(c);
+ mg_call(c, MG_EV_RESOLVE, NULL);
+ rc = connect(FD(c), &usa.sa, slen); // Attempt to connect
+ if (rc == 0) { // Success
+ mg_call(c, MG_EV_CONNECT, NULL); // Send MG_EV_CONNECT to the user
+ } else if (MG_SOCK_PENDING(rc)) { // Need to wait for TCP handshake
+ MG_DEBUG(("%lu %p -> %M pend", c->id, c->fd, mg_print_ip_port, &c->rem));
+ c->is_connecting = 1;
+ } else {
+ mg_error(c, "connect: %d", MG_SOCK_ERR(rc));
+ }
+ }
+}
+
+static MG_SOCKET_TYPE raccept(MG_SOCKET_TYPE sock, union usa *usa,
+ socklen_t *len) {
+ MG_SOCKET_TYPE fd = MG_INVALID_SOCKET;
+ do {
+ memset(usa, 0, sizeof(*usa));
+ fd = accept(sock, &usa->sa, len);
+ } while (MG_SOCK_INTR(fd));
+ return fd;
+}
+
+static void accept_conn(struct mg_mgr *mgr, struct mg_connection *lsn) {
+ struct mg_connection *c = NULL;
+ union usa usa;
+ socklen_t sa_len = sizeof(usa);
+ MG_SOCKET_TYPE fd = raccept(FD(lsn), &usa, &sa_len);
+ if (fd == MG_INVALID_SOCKET) {
+#if MG_ARCH == MG_ARCH_AZURERTOS
+ // AzureRTOS, in non-block socket mode can mark listening socket readable
+ // even it is not. See comment for 'select' func implementation in
+ // nx_bsd.c That's not an error, just should try later
+ if (errno != EAGAIN)
+#endif
+ MG_ERROR(("%lu accept failed, errno %d", lsn->id, MG_SOCK_ERR(-1)));
+#if (MG_ARCH != MG_ARCH_WIN32) && !MG_ENABLE_FREERTOS_TCP && \
+ (MG_ARCH != MG_ARCH_TIRTOS) && !MG_ENABLE_POLL
+ } else if ((long) fd >= FD_SETSIZE) {
+ MG_ERROR(("%ld > %ld", (long) fd, (long) FD_SETSIZE));
+ closesocket(fd);
+#endif
+ } else if ((c = mg_alloc_conn(mgr)) == NULL) {
+ MG_ERROR(("%lu OOM", lsn->id));
+ closesocket(fd);
+ } else {
+ tomgaddr(&usa, &c->rem, sa_len != sizeof(usa.sin));
+ LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c);
+ c->fd = S2PTR(fd);
+ MG_EPOLL_ADD(c);
+ mg_set_non_blocking_mode(FD(c));
+ setsockopts(c);
+ c->is_accepted = 1;
+ c->is_hexdumping = lsn->is_hexdumping;
+ c->loc = lsn->loc;
+ c->pfn = lsn->pfn;
+ c->pfn_data = lsn->pfn_data;
+ c->fn = lsn->fn;
+ c->fn_data = lsn->fn_data;
+ MG_DEBUG(("%lu %p accepted %M -> %M", c->id, c->fd, mg_print_ip_port,
+ &c->rem, mg_print_ip_port, &c->loc));
+ mg_call(c, MG_EV_OPEN, NULL);
+ mg_call(c, MG_EV_ACCEPT, NULL);
+ }
+}
+
+static bool mg_socketpair(MG_SOCKET_TYPE sp[2], union usa usa[2], bool udp) {
+ MG_SOCKET_TYPE sock;
+ socklen_t n = sizeof(usa[0].sin);
+ bool success = false;
+
+ sock = sp[0] = sp[1] = MG_INVALID_SOCKET;
+ (void) memset(&usa[0], 0, sizeof(usa[0]));
+ usa[0].sin.sin_family = AF_INET;
+ *(uint32_t *) &usa->sin.sin_addr = mg_htonl(0x7f000001U); // 127.0.0.1
+ usa[1] = usa[0];
+
+ if (udp && (sp[0] = socket(AF_INET, SOCK_DGRAM, 0)) != MG_INVALID_SOCKET &&
+ (sp[1] = socket(AF_INET, SOCK_DGRAM, 0)) != MG_INVALID_SOCKET &&
+ bind(sp[0], &usa[0].sa, n) == 0 && bind(sp[1], &usa[1].sa, n) == 0 &&
+ getsockname(sp[0], &usa[0].sa, &n) == 0 &&
+ getsockname(sp[1], &usa[1].sa, &n) == 0 &&
+ connect(sp[0], &usa[1].sa, n) == 0 &&
+ connect(sp[1], &usa[0].sa, n) == 0) {
+ success = true;
+ } else if (!udp &&
+ (sock = socket(AF_INET, SOCK_STREAM, 0)) != MG_INVALID_SOCKET &&
+ bind(sock, &usa[0].sa, n) == 0 &&
+ listen(sock, MG_SOCK_LISTEN_BACKLOG_SIZE) == 0 &&
+ getsockname(sock, &usa[0].sa, &n) == 0 &&
+ (sp[0] = socket(AF_INET, SOCK_STREAM, 0)) != MG_INVALID_SOCKET &&
+ connect(sp[0], &usa[0].sa, n) == 0 &&
+ (sp[1] = raccept(sock, &usa[1], &n)) != MG_INVALID_SOCKET) {
+ success = true;
+ }
+ if (success) {
+ mg_set_non_blocking_mode(sp[1]);
+ } else {
+ if (sp[0] != MG_INVALID_SOCKET) closesocket(sp[0]);
+ if (sp[1] != MG_INVALID_SOCKET) closesocket(sp[1]);
+ sp[0] = sp[1] = MG_INVALID_SOCKET;
+ }
+ if (sock != MG_INVALID_SOCKET) closesocket(sock);
+ return success;
+}
+
+int mg_mkpipe(struct mg_mgr *mgr, mg_event_handler_t fn, void *fn_data,
+ bool udp) {
+ union usa usa[2];
+ MG_SOCKET_TYPE sp[2] = {MG_INVALID_SOCKET, MG_INVALID_SOCKET};
+ struct mg_connection *c = NULL;
+ if (!mg_socketpair(sp, usa, udp)) {
+ MG_ERROR(("Cannot create socket pair"));
+ } else if ((c = mg_wrapfd(mgr, (int) sp[1], fn, fn_data)) == NULL) {
+ closesocket(sp[0]);
+ closesocket(sp[1]);
+ sp[0] = sp[1] = MG_INVALID_SOCKET;
+ } else {
+ tomgaddr(&usa[0], &c->rem, false);
+ MG_DEBUG(("%lu %p pipe %lu", c->id, c->fd, (unsigned long) sp[0]));
+ }
+ return (int) sp[0];
+}
+
+static bool can_read(const struct mg_connection *c) {
+ return c->is_full == false;
+}
+
+static bool can_write(const struct mg_connection *c) {
+ return c->is_connecting || (c->send.len > 0 && c->is_tls_hs == 0);
+}
+
+static bool skip_iotest(const struct mg_connection *c) {
+ return (c->is_closing || c->is_resolving || FD(c) == MG_INVALID_SOCKET) ||
+ (can_read(c) == false && can_write(c) == false);
+}
+
+static void mg_iotest(struct mg_mgr *mgr, int ms) {
+#if MG_ENABLE_FREERTOS_TCP
+ struct mg_connection *c;
+ for (c = mgr->conns; c != NULL; c = c->next) {
+ c->is_readable = c->is_writable = 0;
+ if (skip_iotest(c)) continue;
+ if (can_read(c))
+ FreeRTOS_FD_SET(c->fd, mgr->ss, eSELECT_READ | eSELECT_EXCEPT);
+ if (can_write(c)) FreeRTOS_FD_SET(c->fd, mgr->ss, eSELECT_WRITE);
+ }
+ FreeRTOS_select(mgr->ss, pdMS_TO_TICKS(ms));
+ for (c = mgr->conns; c != NULL; c = c->next) {
+ EventBits_t bits = FreeRTOS_FD_ISSET(c->fd, mgr->ss);
+ c->is_readable = bits & (eSELECT_READ | eSELECT_EXCEPT) ? 1U : 0;
+ c->is_writable = bits & eSELECT_WRITE ? 1U : 0;
+ if (c->fd != MG_INVALID_SOCKET)
+ FreeRTOS_FD_CLR(c->fd, mgr->ss,
+ eSELECT_READ | eSELECT_EXCEPT | eSELECT_WRITE);
+ }
+#elif MG_ENABLE_EPOLL
+ size_t max = 1;
+ for (struct mg_connection *c = mgr->conns; c != NULL; c = c->next) {
+ c->is_readable = c->is_writable = 0;
+ if (mg_tls_pending(c) > 0) ms = 1, c->is_readable = 1;
+ if (can_write(c)) MG_EPOLL_MOD(c, 1);
+ max++;
+ }
+ struct epoll_event *evs = (struct epoll_event *) alloca(max * sizeof(evs[0]));
+ int n = epoll_wait(mgr->epoll_fd, evs, (int) max, ms);
+ for (int i = 0; i < n; i++) {
+ struct mg_connection *c = (struct mg_connection *) evs[i].data.ptr;
+ if (evs[i].events & EPOLLERR) {
+ mg_error(c, "socket error");
+ } else if (c->is_readable == 0) {
+ bool rd = evs[i].events & (EPOLLIN | EPOLLHUP);
+ bool wr = evs[i].events & EPOLLOUT;
+ c->is_readable = can_read(c) && rd ? 1U : 0;
+ c->is_writable = can_write(c) && wr ? 1U : 0;
+ }
+ }
+ (void) skip_iotest;
+#elif MG_ENABLE_POLL
+ nfds_t n = 0;
+ for (struct mg_connection *c = mgr->conns; c != NULL; c = c->next) n++;
+ struct pollfd *fds = (struct pollfd *) alloca(n * sizeof(fds[0]));
+ memset(fds, 0, n * sizeof(fds[0]));
+ n = 0;
+ for (struct mg_connection *c = mgr->conns; c != NULL; c = c->next) {
+ c->is_readable = c->is_writable = 0;
+ if (skip_iotest(c)) {
+ // Socket not valid, ignore
+ } else if (mg_tls_pending(c) > 0) {
+ ms = 1; // Don't wait if TLS is ready
+ } else {
+ fds[n].fd = FD(c);
+ if (can_read(c)) fds[n].events |= POLLIN;
+ if (can_write(c)) fds[n].events |= POLLOUT;
+ n++;
+ }
+ }
+
+ // MG_INFO(("poll n=%d ms=%d", (int) n, ms));
+ if (poll(fds, n, ms) < 0) {
+#if MG_ARCH == MG_ARCH_WIN32
+ if (n == 0) Sleep(ms); // On Windows, poll fails if no sockets
+#endif
+ memset(fds, 0, n * sizeof(fds[0]));
+ }
+ n = 0;
+ for (struct mg_connection *c = mgr->conns; c != NULL; c = c->next) {
+ if (skip_iotest(c)) {
+ // Socket not valid, ignore
+ } else if (mg_tls_pending(c) > 0) {
+ c->is_readable = 1;
+ } else {
+ if (fds[n].revents & POLLERR) {
+ mg_error(c, "socket error");
+ } else {
+ c->is_readable =
+ (unsigned) (fds[n].revents & (POLLIN | POLLHUP) ? 1 : 0);
+ c->is_writable = (unsigned) (fds[n].revents & POLLOUT ? 1 : 0);
+ }
+ n++;
+ }
+ }
+#else
+ struct timeval tv = {ms / 1000, (ms % 1000) * 1000}, tv_zero = {0, 0}, *tvp;
+ struct mg_connection *c;
+ fd_set rset, wset, eset;
+ MG_SOCKET_TYPE maxfd = 0;
+ int rc;
+
+ FD_ZERO(&rset);
+ FD_ZERO(&wset);
+ FD_ZERO(&eset);
+ tvp = ms < 0 ? NULL : &tv;
+ for (c = mgr->conns; c != NULL; c = c->next) {
+ c->is_readable = c->is_writable = 0;
+ if (skip_iotest(c)) continue;
+ FD_SET(FD(c), &eset);
+ if (can_read(c)) FD_SET(FD(c), &rset);
+ if (can_write(c)) FD_SET(FD(c), &wset);
+ if (mg_tls_pending(c) > 0) tvp = &tv_zero;
+ if (FD(c) > maxfd) maxfd = FD(c);
+ }
+
+ if ((rc = select((int) maxfd + 1, &rset, &wset, &eset, tvp)) < 0) {
+#if MG_ARCH == MG_ARCH_WIN32
+ if (maxfd == 0) Sleep(ms); // On Windows, select fails if no sockets
+#else
+ MG_ERROR(("select: %d %d", rc, MG_SOCK_ERR(rc)));
+#endif
+ FD_ZERO(&rset);
+ FD_ZERO(&wset);
+ FD_ZERO(&eset);
+ }
+
+ for (c = mgr->conns; c != NULL; c = c->next) {
+ if (FD(c) != MG_INVALID_SOCKET && FD_ISSET(FD(c), &eset)) {
+ mg_error(c, "socket error");
+ } else {
+ c->is_readable = FD(c) != MG_INVALID_SOCKET && FD_ISSET(FD(c), &rset);
+ c->is_writable = FD(c) != MG_INVALID_SOCKET && FD_ISSET(FD(c), &wset);
+ if (mg_tls_pending(c) > 0) c->is_readable = 1;
+ }
+ }
+#endif
+}
+
+void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
+ struct mg_connection *c, *tmp;
+ uint64_t now;
+
+ mg_iotest(mgr, ms);
+ now = mg_millis();
+ mg_timer_poll(&mgr->timers, now);
+
+ for (c = mgr->conns; c != NULL; c = tmp) {
+ bool is_resp = c->is_resp;
+ tmp = c->next;
+ mg_call(c, MG_EV_POLL, &now);
+ if (is_resp && !c->is_resp) {
+ long n = 0;
+ mg_call(c, MG_EV_READ, &n);
+ }
+ MG_VERBOSE(("%lu %c%c %c%c%c%c%c", c->id, c->is_readable ? 'r' : '-',
+ c->is_writable ? 'w' : '-', c->is_tls ? 'T' : 't',
+ c->is_connecting ? 'C' : 'c', c->is_tls_hs ? 'H' : 'h',
+ c->is_resolving ? 'R' : 'r', c->is_closing ? 'C' : 'c'));
+ if (c->is_resolving || c->is_closing) {
+ // Do nothing
+ } else if (c->is_listening && c->is_udp == 0) {
+ if (c->is_readable) accept_conn(mgr, c);
+ } else if (c->is_connecting) {
+ if (c->is_readable || c->is_writable) connect_conn(c);
+ } else if (c->is_tls_hs) {
+ if ((c->is_readable || c->is_writable)) mg_tls_handshake(c);
+ } else {
+ if (c->is_readable) read_conn(c);
+ if (c->is_writable) write_conn(c);
+ }
+
+ if (c->is_draining && c->send.len == 0) c->is_closing = 1;
+ if (c->is_closing) close_conn(c);
+ }
+}
+#endif
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/ssi.c"
+#endif
+
+
+
+
+#ifndef MG_MAX_SSI_DEPTH
+#define MG_MAX_SSI_DEPTH 5
+#endif
+
+#ifndef MG_SSI_BUFSIZ
+#define MG_SSI_BUFSIZ 1024
+#endif
+
+#if MG_ENABLE_SSI
+static char *mg_ssi(const char *path, const char *root, int depth) {
+ struct mg_iobuf b = {NULL, 0, 0, MG_IO_SIZE};
+ FILE *fp = fopen(path, "rb");
+ if (fp != NULL) {
+ char buf[MG_SSI_BUFSIZ], arg[sizeof(buf)];
+ int ch, intag = 0;
+ size_t len = 0;
+ buf[0] = arg[0] = '\0';
+ while ((ch = fgetc(fp)) != EOF) {
+ if (intag && ch == '>' && buf[len - 1] == '-' && buf[len - 2] == '-') {
+ buf[len++] = (char) (ch & 0xff);
+ buf[len] = '\0';
+ if (sscanf(buf, "<!--#include file=\"%[^\"]", arg)) {
+ char tmp[MG_PATH_MAX + MG_SSI_BUFSIZ + 10],
+ *p = (char *) path + strlen(path), *data;
+ while (p > path && p[-1] != MG_DIRSEP && p[-1] != '/') p--;
+ mg_snprintf(tmp, sizeof(tmp), "%.*s%s", (int) (p - path), path, arg);
+ if (depth < MG_MAX_SSI_DEPTH &&
+ (data = mg_ssi(tmp, root, depth + 1)) != NULL) {
+ mg_iobuf_add(&b, b.len, data, strlen(data));
+ free(data);
+ } else {
+ MG_ERROR(("%s: file=%s error or too deep", path, arg));
+ }
+ } else if (sscanf(buf, "<!--#include virtual=\"%[^\"]", arg)) {
+ char tmp[MG_PATH_MAX + MG_SSI_BUFSIZ + 10], *data;
+ mg_snprintf(tmp, sizeof(tmp), "%s%s", root, arg);
+ if (depth < MG_MAX_SSI_DEPTH &&
+ (data = mg_ssi(tmp, root, depth + 1)) != NULL) {
+ mg_iobuf_add(&b, b.len, data, strlen(data));
+ free(data);
+ } else {
+ MG_ERROR(("%s: virtual=%s error or too deep", path, arg));
+ }
+ } else {
+ // Unknown SSI tag
+ MG_ERROR(("Unknown SSI tag: %.*s", (int) len, buf));
+ mg_iobuf_add(&b, b.len, buf, len);
+ }
+ intag = 0;
+ len = 0;
+ } else if (ch == '<') {
+ intag = 1;
+ if (len > 0) mg_iobuf_add(&b, b.len, buf, len);
+ len = 0;
+ buf[len++] = (char) (ch & 0xff);
+ } else if (intag) {
+ if (len == 5 && strncmp(buf, "<!--#", 5) != 0) {
+ intag = 0;
+ } else if (len >= sizeof(buf) - 2) {
+ MG_ERROR(("%s: SSI tag is too large", path));
+ len = 0;
+ }
+ buf[len++] = (char) (ch & 0xff);
+ } else {
+ buf[len++] = (char) (ch & 0xff);
+ if (len >= sizeof(buf)) {
+ mg_iobuf_add(&b, b.len, buf, len);
+ len = 0;
+ }
+ }
+ }
+ if (len > 0) mg_iobuf_add(&b, b.len, buf, len);
+ if (b.len > 0) mg_iobuf_add(&b, b.len, "", 1); // nul-terminate
+ fclose(fp);
+ }
+ (void) depth;
+ (void) root;
+ return (char *) b.buf;
+}
+
+void mg_http_serve_ssi(struct mg_connection *c, const char *root,
+ const char *fullpath) {
+ const char *headers = "Content-Type: text/html; charset=utf-8\r\n";
+ char *data = mg_ssi(fullpath, root, 0);
+ mg_http_reply(c, 200, headers, "%s", data == NULL ? "" : data);
+ free(data);
+}
+#else
+void mg_http_serve_ssi(struct mg_connection *c, const char *root,
+ const char *fullpath) {
+ mg_http_reply(c, 501, NULL, "SSI not enabled");
+ (void) root, (void) fullpath;
+}
+#endif
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/str.c"
+#endif
+
+
+struct mg_str mg_str_s(const char *s) {
+ struct mg_str str = {s, s == NULL ? 0 : strlen(s)};
+ return str;
+}
+
+struct mg_str mg_str_n(const char *s, size_t n) {
+ struct mg_str str = {s, n};
+ return str;
+}
+
+int mg_lower(const char *s) {
+ int c = *s;
+ if (c >= 'A' && c <= 'Z') c += 'a' - 'A';
+ return c;
+}
+
+int mg_ncasecmp(const char *s1, const char *s2, size_t len) {
+ int diff = 0;
+ if (len > 0) do {
+ diff = mg_lower(s1++) - mg_lower(s2++);
+ } while (diff == 0 && s1[-1] != '\0' && --len > 0);
+ return diff;
+}
+
+int mg_casecmp(const char *s1, const char *s2) {
+ return mg_ncasecmp(s1, s2, (size_t) ~0);
+}
+
+int mg_vcmp(const struct mg_str *s1, const char *s2) {
+ size_t n2 = strlen(s2), n1 = s1->len;
+ int r = strncmp(s1->ptr, s2, (n1 < n2) ? n1 : n2);
+ if (r == 0) return (int) (n1 - n2);
+ return r;
+}
+
+int mg_vcasecmp(const struct mg_str *str1, const char *str2) {
+ size_t n2 = strlen(str2), n1 = str1->len;
+ int r = mg_ncasecmp(str1->ptr, str2, (n1 < n2) ? n1 : n2);
+ if (r == 0) return (int) (n1 - n2);
+ return r;
+}
+
+struct mg_str mg_strdup(const struct mg_str s) {
+ struct mg_str r = {NULL, 0};
+ if (s.len > 0 && s.ptr != NULL) {
+ char *sc = (char *) calloc(1, s.len + 1);
+ if (sc != NULL) {
+ memcpy(sc, s.ptr, s.len);
+ sc[s.len] = '\0';
+ r.ptr = sc;
+ r.len = s.len;
+ }
+ }
+ return r;
+}
+
+int mg_strcmp(const struct mg_str str1, const struct mg_str str2) {
+ size_t i = 0;
+ while (i < str1.len && i < str2.len) {
+ int c1 = str1.ptr[i];
+ int c2 = str2.ptr[i];
+ if (c1 < c2) return -1;
+ if (c1 > c2) return 1;
+ i++;
+ }
+ if (i < str1.len) return 1;
+ if (i < str2.len) return -1;
+ return 0;
+}
+
+const char *mg_strstr(const struct mg_str haystack,
+ const struct mg_str needle) {
+ size_t i;
+ if (needle.len > haystack.len) return NULL;
+ if (needle.len == 0) return haystack.ptr;
+ for (i = 0; i <= haystack.len - needle.len; i++) {
+ if (memcmp(haystack.ptr + i, needle.ptr, needle.len) == 0) {
+ return haystack.ptr + i;
+ }
+ }
+ return NULL;
+}
+
+static bool is_space(int c) {
+ return c == ' ' || c == '\r' || c == '\n' || c == '\t';
+}
+
+struct mg_str mg_strstrip(struct mg_str s) {
+ while (s.len > 0 && is_space((int) *s.ptr)) s.ptr++, s.len--;
+ while (s.len > 0 && is_space((int) *(s.ptr + s.len - 1))) s.len--;
+ return s;
+}
+
+bool mg_match(struct mg_str s, struct mg_str p, struct mg_str *caps) {
+ size_t i = 0, j = 0, ni = 0, nj = 0;
+ if (caps) caps->ptr = NULL, caps->len = 0;
+ while (i < p.len || j < s.len) {
+ if (i < p.len && j < s.len && (p.ptr[i] == '?' || s.ptr[j] == p.ptr[i])) {
+ if (caps == NULL) {
+ } else if (p.ptr[i] == '?') {
+ caps->ptr = &s.ptr[j], caps->len = 1; // Finalize `?` cap
+ caps++, caps->ptr = NULL, caps->len = 0; // Init next cap
+ } else if (caps->ptr != NULL && caps->len == 0) {
+ caps->len = (size_t) (&s.ptr[j] - caps->ptr); // Finalize current cap
+ caps++, caps->len = 0, caps->ptr = NULL; // Init next cap
+ }
+ i++, j++;
+ } else if (i < p.len && (p.ptr[i] == '*' || p.ptr[i] == '#')) {
+ if (caps && !caps->ptr) caps->len = 0, caps->ptr = &s.ptr[j]; // Init cap
+ ni = i++, nj = j + 1;
+ } else if (nj > 0 && nj <= s.len && (p.ptr[ni] == '#' || s.ptr[j] != '/')) {
+ i = ni, j = nj;
+ if (caps && caps->ptr == NULL && caps->len == 0) {
+ caps--, caps->len = 0; // Restart previous cap
+ }
+ } else {
+ return false;
+ }
+ }
+ if (caps && caps->ptr && caps->len == 0) {
+ caps->len = (size_t) (&s.ptr[j] - caps->ptr);
+ }
+ return true;
+}
+
+bool mg_globmatch(const char *s1, size_t n1, const char *s2, size_t n2) {
+ return mg_match(mg_str_n(s2, n2), mg_str_n(s1, n1), NULL);
+}
+
+static size_t mg_nce(const char *s, size_t n, size_t ofs, size_t *koff,
+ size_t *klen, size_t *voff, size_t *vlen, char delim) {
+ size_t kvlen, kl;
+ for (kvlen = 0; ofs + kvlen < n && s[ofs + kvlen] != delim;) kvlen++;
+ for (kl = 0; kl < kvlen && s[ofs + kl] != '=';) kl++;
+ if (koff != NULL) *koff = ofs;
+ if (klen != NULL) *klen = kl;
+ if (voff != NULL) *voff = kl < kvlen ? ofs + kl + 1 : 0;
+ if (vlen != NULL) *vlen = kl < kvlen ? kvlen - kl - 1 : 0;
+ ofs += kvlen + 1;
+ return ofs > n ? n : ofs;
+}
+
+bool mg_split(struct mg_str *s, struct mg_str *k, struct mg_str *v, char sep) {
+ size_t koff = 0, klen = 0, voff = 0, vlen = 0, off = 0;
+ if (s->ptr == NULL || s->len == 0) return 0;
+ off = mg_nce(s->ptr, s->len, 0, &koff, &klen, &voff, &vlen, sep);
+ if (k != NULL) *k = mg_str_n(s->ptr + koff, klen);
+ if (v != NULL) *v = mg_str_n(s->ptr + voff, vlen);
+ *s = mg_str_n(s->ptr + off, s->len - off);
+ return off > 0;
+}
+
+bool mg_commalist(struct mg_str *s, struct mg_str *k, struct mg_str *v) {
+ return mg_split(s, k, v, ',');
+}
+
+char *mg_hex(const void *buf, size_t len, char *to) {
+ const unsigned char *p = (const unsigned char *) buf;
+ const char *hex = "0123456789abcdef";
+ size_t i = 0;
+ for (; len--; p++) {
+ to[i++] = hex[p[0] >> 4];
+ to[i++] = hex[p[0] & 0x0f];
+ }
+ to[i] = '\0';
+ return to;
+}
+
+static unsigned char mg_unhex_nimble(unsigned char c) {
+ return (c >= '0' && c <= '9') ? (unsigned char) (c - '0')
+ : (c >= 'A' && c <= 'F') ? (unsigned char) (c - '7')
+ : (unsigned char) (c - 'W');
+}
+
+unsigned long mg_unhexn(const char *s, size_t len) {
+ unsigned long i = 0, v = 0;
+ for (i = 0; i < len; i++) v <<= 4, v |= mg_unhex_nimble(((uint8_t *) s)[i]);
+ return v;
+}
+
+void mg_unhex(const char *buf, size_t len, unsigned char *to) {
+ size_t i;
+ for (i = 0; i < len; i += 2) {
+ to[i >> 1] = (unsigned char) mg_unhexn(&buf[i], 2);
+ }
+}
+
+char *mg_remove_double_dots(char *s) {
+ char *saved = s, *p = s;
+ while (*s != '\0') {
+ *p++ = *s++;
+ if (s[-1] == '/' || s[-1] == '\\') {
+ while (s[0] != '\0') {
+ if (s[0] == '/' || s[0] == '\\') {
+ s++;
+ } else if (s[0] == '.' && s[1] == '.' &&
+ (s[2] == '/' || s[2] == '\\')) {
+ s += 2;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ *p = '\0';
+ return saved;
+}
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/timer.c"
+#endif
+
+
+
+#define MG_TIMER_CALLED 4
+
+void mg_timer_init(struct mg_timer **head, struct mg_timer *t, uint64_t ms,
+ unsigned flags, void (*fn)(void *), void *arg) {
+ t->id = 0, t->period_ms = ms, t->expire = 0;
+ t->flags = flags, t->fn = fn, t->arg = arg, t->next = *head;
+ *head = t;
+}
+
+void mg_timer_free(struct mg_timer **head, struct mg_timer *t) {
+ while (*head && *head != t) head = &(*head)->next;
+ if (*head) *head = t->next;
+}
+
+// t: expiration time, prd: period, now: current time. Return true if expired
+bool mg_timer_expired(uint64_t *t, uint64_t prd, uint64_t now) {
+ if (now + prd < *t) *t = 0; // Time wrapped? Reset timer
+ if (*t == 0) *t = now + prd; // Firt poll? Set expiration
+ if (*t > now) return false; // Not expired yet, return
+ *t = (now - *t) > prd ? now + prd : *t + prd; // Next expiration time
+ return true; // Expired, return true
+}
+
+void mg_timer_poll(struct mg_timer **head, uint64_t now_ms) {
+ struct mg_timer *t, *tmp;
+ for (t = *head; t != NULL; t = tmp) {
+ bool once = t->expire == 0 && (t->flags & MG_TIMER_RUN_NOW) &&
+ !(t->flags & MG_TIMER_CALLED); // Handle MG_TIMER_NOW only once
+ bool expired = mg_timer_expired(&t->expire, t->period_ms, now_ms);
+ tmp = t->next;
+ if (!once && !expired) continue;
+ if ((t->flags & MG_TIMER_REPEAT) || !(t->flags & MG_TIMER_CALLED)) {
+ t->fn(t->arg);
+ }
+ t->flags |= MG_TIMER_CALLED;
+ }
+}
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/tls_dummy.c"
+#endif
+
+
+#if !MG_ENABLE_MBEDTLS && !MG_ENABLE_OPENSSL && !MG_ENABLE_CUSTOM_TLS
+void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
+ (void) opts;
+ mg_error(c, "TLS is not enabled");
+}
+void mg_tls_handshake(struct mg_connection *c) {
+ (void) c;
+}
+void mg_tls_free(struct mg_connection *c) {
+ (void) c;
+}
+long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) {
+ return c == NULL || buf == NULL || len == 0 ? 0 : -1;
+}
+long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) {
+ return c == NULL || buf == NULL || len == 0 ? 0 : -1;
+}
+size_t mg_tls_pending(struct mg_connection *c) {
+ (void) c;
+ return 0;
+}
+#endif
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/tls_mbed.c"
+#endif
+
+
+
+
+#if MG_ENABLE_MBEDTLS
+
+#if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= 0x03000000
+#define MGRNG , rng_get, NULL
+#else
+#define MGRNG
+#endif
+
+void mg_tls_free(struct mg_connection *c) {
+ struct mg_tls *tls = (struct mg_tls *) c->tls;
+ if (tls != NULL) {
+ free(tls->cafile);
+ mbedtls_ssl_free(&tls->ssl);
+ mbedtls_pk_free(&tls->pk);
+ mbedtls_x509_crt_free(&tls->ca);
+ mbedtls_x509_crt_free(&tls->cert);
+ mbedtls_ssl_config_free(&tls->conf);
+ free(tls);
+ c->tls = NULL;
+ }
+}
+
+static int mg_net_send(void *ctx, const unsigned char *buf, size_t len) {
+ long n = mg_io_send((struct mg_connection *) ctx, buf, len);
+ MG_VERBOSE(("%lu n=%ld e=%d", ((struct mg_connection *) ctx)->id, n, errno));
+ if (n == MG_IO_WAIT) return MBEDTLS_ERR_SSL_WANT_WRITE;
+ if (n == MG_IO_RESET) return MBEDTLS_ERR_NET_CONN_RESET;
+ if (n == MG_IO_ERR) return MBEDTLS_ERR_NET_SEND_FAILED;
+ return (int) n;
+}
+
+static int mg_net_recv(void *ctx, unsigned char *buf, size_t len) {
+ long n = mg_io_recv((struct mg_connection *) ctx, buf, len);
+ MG_VERBOSE(("%lu n=%ld", ((struct mg_connection *) ctx)->id, n));
+ if (n == MG_IO_WAIT) return MBEDTLS_ERR_SSL_WANT_WRITE;
+ if (n == MG_IO_RESET) return MBEDTLS_ERR_NET_CONN_RESET;
+ if (n == MG_IO_ERR) return MBEDTLS_ERR_NET_RECV_FAILED;
+ return (int) n;
+}
+
+void mg_tls_handshake(struct mg_connection *c) {
+ struct mg_tls *tls = (struct mg_tls *) c->tls;
+ int rc = mbedtls_ssl_handshake(&tls->ssl);
+ if (rc == 0) { // Success
+ MG_DEBUG(("%lu success", c->id));
+ c->is_tls_hs = 0;
+ mg_call(c, MG_EV_TLS_HS, NULL);
+ } else if (rc == MBEDTLS_ERR_SSL_WANT_READ ||
+ rc == MBEDTLS_ERR_SSL_WANT_WRITE) { // Still pending
+ MG_VERBOSE(("%lu pending, %d%d %d (-%#x)", c->id, c->is_connecting,
+ c->is_tls_hs, rc, -rc));
+ } else {
+ mg_error(c, "TLS handshake: -%#x", -rc); // Error
+ }
+}
+
+static int mbed_rng(void *ctx, unsigned char *buf, size_t len) {
+ mg_random(buf, len);
+ (void) ctx;
+ return 0;
+}
+
+static void debug_cb(void *c, int lev, const char *s, int n, const char *s2) {
+ n = (int) strlen(s2) - 1;
+ MG_INFO(("%lu %d %.*s", ((struct mg_connection *) c)->id, lev, n, s2));
+ (void) s;
+}
+
+#if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= 0x03000000
+static int rng_get(void *p_rng, unsigned char *buf, size_t len) {
+ (void) p_rng;
+ mg_random(buf, len);
+ return 0;
+}
+#endif
+
+static struct mg_str mg_loadfile(struct mg_fs *fs, const char *path) {
+ size_t n = 0;
+ if (path[0] == '-') return mg_str(path);
+ char *p = mg_file_read(fs, path, &n);
+ return mg_str_n(p, n);
+}
+
+void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
+ struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs;
+ struct mg_tls *tls = (struct mg_tls *) calloc(1, sizeof(*tls));
+ int rc = 0;
+ c->tls = tls;
+ if (c->tls == NULL) {
+ mg_error(c, "TLS OOM");
+ goto fail;
+ }
+ MG_DEBUG(("%lu Setting TLS", c->id));
+ mbedtls_ssl_init(&tls->ssl);
+ mbedtls_ssl_config_init(&tls->conf);
+ mbedtls_x509_crt_init(&tls->ca);
+ mbedtls_x509_crt_init(&tls->cert);
+ mbedtls_pk_init(&tls->pk);
+ mbedtls_ssl_conf_dbg(&tls->conf, debug_cb, c);
+#if defined(MG_MBEDTLS_DEBUG_LEVEL)
+ mbedtls_debug_set_threshold(MG_MBEDTLS_DEBUG_LEVEL);
+#endif
+ if ((rc = mbedtls_ssl_config_defaults(
+ &tls->conf,
+ c->is_client ? MBEDTLS_SSL_IS_CLIENT : MBEDTLS_SSL_IS_SERVER,
+ MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT)) != 0) {
+ mg_error(c, "tls defaults %#x", -rc);
+ goto fail;
+ }
+ mbedtls_ssl_conf_rng(&tls->conf, mbed_rng, c);
+ if (opts->ca == NULL || strcmp(opts->ca, "*") == 0) {
+ mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_NONE);
+ } else if (opts->ca != NULL && opts->ca[0] != '\0') {
+#if defined(MBEDTLS_X509_CA_CHAIN_ON_DISK)
+ tls->cafile = strdup(opts->ca);
+ rc = mbedtls_ssl_conf_ca_chain_file(&tls->conf, tls->cafile, NULL);
+ if (rc != 0) {
+ mg_error(c, "parse on-disk chain(%s) err %#x", tls->cafile, -rc);
+ goto fail;
+ }
+#else
+ struct mg_str s = mg_loadfile(fs, opts->ca);
+ rc = mbedtls_x509_crt_parse(&tls->ca, (uint8_t *) s.ptr, s.len + 1);
+ if (opts->ca[0] != '-') free((char *) s.ptr);
+ if (rc != 0) {
+ mg_error(c, "parse(%s) err %#x", opts->ca, -rc);
+ goto fail;
+ }
+ mbedtls_ssl_conf_ca_chain(&tls->conf, &tls->ca, NULL);
+#endif
+ if (opts->srvname.len > 0) {
+ char *x = mg_mprintf("%.*s", (int) opts->srvname.len, opts->srvname.ptr);
+ mbedtls_ssl_set_hostname(&tls->ssl, x);
+ free(x);
+ }
+ mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_REQUIRED);
+ }
+ if (opts->cert != NULL && opts->cert[0] != '\0') {
+ struct mg_str s = mg_loadfile(fs, opts->cert);
+ const char *key = opts->certkey == NULL ? opts->cert : opts->certkey;
+ rc = mbedtls_x509_crt_parse(&tls->cert, (uint8_t *) s.ptr, s.len + 1);
+ if (opts->cert[0] != '-') free((char *) s.ptr);
+ if (rc != 0) {
+ mg_error(c, "parse(%s) err %#x", opts->cert, -rc);
+ goto fail;
+ }
+ s = mg_loadfile(fs, key);
+ rc = mbedtls_pk_parse_key(&tls->pk, (uint8_t *) s.ptr, s.len + 1, NULL,
+ 0 MGRNG);
+ if (key[0] != '-') free((char *) s.ptr);
+ if (rc != 0) {
+ mg_error(c, "tls key(%s) %#x", key, -rc);
+ goto fail;
+ }
+ rc = mbedtls_ssl_conf_own_cert(&tls->conf, &tls->cert, &tls->pk);
+ if (rc != 0) {
+ mg_error(c, "own cert %#x", -rc);
+ goto fail;
+ }
+ }
+ if ((rc = mbedtls_ssl_setup(&tls->ssl, &tls->conf)) != 0) {
+ mg_error(c, "setup err %#x", -rc);
+ goto fail;
+ }
+ c->tls = tls;
+ c->is_tls = 1;
+ c->is_tls_hs = 1;
+ mbedtls_ssl_set_bio(&tls->ssl, c, mg_net_send, mg_net_recv, 0);
+ if (c->is_client && c->is_resolving == 0 && c->is_connecting == 0) {
+ mg_tls_handshake(c);
+ }
+ return;
+fail:
+ mg_tls_free(c);
+}
+
+size_t mg_tls_pending(struct mg_connection *c) {
+ struct mg_tls *tls = (struct mg_tls *) c->tls;
+ return tls == NULL ? 0 : mbedtls_ssl_get_bytes_avail(&tls->ssl);
+}
+
+long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) {
+ struct mg_tls *tls = (struct mg_tls *) c->tls;
+ long n = mbedtls_ssl_read(&tls->ssl, (unsigned char *) buf, len);
+ if (n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE)
+ return MG_IO_WAIT;
+ if (n <= 0) return MG_IO_ERR;
+ return n;
+}
+
+long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) {
+ struct mg_tls *tls = (struct mg_tls *) c->tls;
+ long n = mbedtls_ssl_write(&tls->ssl, (unsigned char *) buf, len);
+ if (n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE)
+ return MG_IO_WAIT;
+ if (n <= 0) return MG_IO_ERR;
+ return n;
+}
+#endif
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/tls_openssl.c"
+#endif
+
+
+
+#if MG_ENABLE_OPENSSL
+static int mg_tls_err(struct mg_tls *tls, int res) {
+ int err = SSL_get_error(tls->ssl, res);
+ // We've just fetched the last error from the queue.
+ // Now we need to clear the error queue. If we do not, then the following
+ // can happen (actually reported):
+ // - A new connection is accept()-ed with cert error (e.g. self-signed cert)
+ // - Since all accept()-ed connections share listener's context,
+ // - *ALL* SSL accepted connection report read error on the next poll cycle.
+ // Thus a single errored connection can close all the rest, unrelated ones.
+ // Clearing the error keeps the shared SSL_CTX in an OK state.
+
+ if (err != 0) ERR_print_errors_fp(stderr);
+ ERR_clear_error();
+ if (err == SSL_ERROR_WANT_READ) return 0;
+ if (err == SSL_ERROR_WANT_WRITE) return 0;
+ return err;
+}
+
+void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
+ struct mg_tls *tls = (struct mg_tls *) calloc(1, sizeof(*tls));
+ const char *id = "mongoose";
+ static unsigned char s_initialised = 0;
+ int rc;
+
+ if (tls == NULL) {
+ mg_error(c, "TLS OOM");
+ goto fail;
+ }
+
+ if (!s_initialised) {
+ SSL_library_init();
+ s_initialised++;
+ }
+ MG_DEBUG(("%lu Setting TLS, CA: %s, cert: %s, key: %s", c->id,
+ opts->ca == NULL ? "null" : opts->ca,
+ opts->cert == NULL ? "null" : opts->cert,
+ opts->certkey == NULL ? "null" : opts->certkey));
+ tls->ctx = c->is_client ? SSL_CTX_new(SSLv23_client_method())
+ : SSL_CTX_new(SSLv23_server_method());
+ if ((tls->ssl = SSL_new(tls->ctx)) == NULL) {
+ mg_error(c, "SSL_new");
+ goto fail;
+ }
+ SSL_set_session_id_context(tls->ssl, (const uint8_t *) id,
+ (unsigned) strlen(id));
+ // Disable deprecated protocols
+ SSL_set_options(tls->ssl, SSL_OP_NO_SSLv2);
+ SSL_set_options(tls->ssl, SSL_OP_NO_SSLv3);
+ SSL_set_options(tls->ssl, SSL_OP_NO_TLSv1);
+ SSL_set_options(tls->ssl, SSL_OP_NO_TLSv1_1);
+#ifdef MG_ENABLE_OPENSSL_NO_COMPRESSION
+ SSL_set_options(tls->ssl, SSL_OP_NO_COMPRESSION);
+#endif
+#ifdef MG_ENABLE_OPENSSL_CIPHER_SERVER_PREFERENCE
+ SSL_set_options(tls->ssl, SSL_OP_CIPHER_SERVER_PREFERENCE);
+#endif
+
+ if (opts->ca != NULL && opts->ca[0] != '\0') {
+ SSL_set_verify(tls->ssl, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
+ NULL);
+ if ((rc = SSL_CTX_load_verify_locations(tls->ctx, opts->ca, NULL)) != 1) {
+ mg_error(c, "load('%s') %d err %d", opts->ca, rc, mg_tls_err(tls, rc));
+ goto fail;
+ }
+ }
+ if (opts->cert != NULL && opts->cert[0] != '\0') {
+ const char *key = opts->certkey;
+ if (key == NULL) key = opts->cert;
+ if ((rc = SSL_use_certificate_file(tls->ssl, opts->cert, 1)) != 1) {
+ mg_error(c, "Invalid SSL cert, err %d", mg_tls_err(tls, rc));
+ goto fail;
+ } else if ((rc = SSL_use_PrivateKey_file(tls->ssl, key, 1)) != 1) {
+ mg_error(c, "Invalid SSL key, err %d", mg_tls_err(tls, rc));
+ goto fail;
+#if OPENSSL_VERSION_NUMBER > 0x10100000L
+ } else if ((rc = SSL_use_certificate_chain_file(tls->ssl, opts->cert)) !=
+ 1) {
+ mg_error(c, "Invalid chain, err %d", mg_tls_err(tls, rc));
+ goto fail;
+#endif
+ } else {
+ SSL_set_mode(tls->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+#if OPENSSL_VERSION_NUMBER > 0x10002000L
+ SSL_set_ecdh_auto(tls->ssl, 1);
+#endif
+ }
+ }
+ if (opts->ciphers != NULL) SSL_set_cipher_list(tls->ssl, opts->ciphers);
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ if (opts->srvname.len > 0) {
+ char *s = mg_mprintf("%.*s", (int) opts->srvname.len, opts->srvname.ptr);
+ SSL_set1_host(tls->ssl, s);
+ SSL_set_tlsext_host_name(tls->ssl, s);
+ free(s);
+ }
+#endif
+ c->tls = tls;
+ c->is_tls = 1;
+ c->is_tls_hs = 1;
+ if (c->is_client && c->is_resolving == 0 && c->is_connecting == 0) {
+ mg_tls_handshake(c);
+ }
+ MG_DEBUG(("%lu SSL %s OK", c->id, c->is_accepted ? "accept" : "client"));
+ return;
+fail:
+ c->is_closing = 1;
+ free(tls);
+}
+
+void mg_tls_handshake(struct mg_connection *c) {
+ struct mg_tls *tls = (struct mg_tls *) c->tls;
+ int rc;
+ SSL_set_fd(tls->ssl, (int) (size_t) c->fd);
+ rc = c->is_client ? SSL_connect(tls->ssl) : SSL_accept(tls->ssl);
+ if (rc == 1) {
+ MG_DEBUG(("%lu success", c->id));
+ c->is_tls_hs = 0;
+ mg_call(c, MG_EV_TLS_HS, NULL);
+ } else {
+ int code = mg_tls_err(tls, rc);
+ if (code != 0) mg_error(c, "tls hs: rc %d, err %d", rc, code);
+ }
+}
+
+void mg_tls_free(struct mg_connection *c) {
+ struct mg_tls *tls = (struct mg_tls *) c->tls;
+ if (tls == NULL) return;
+ SSL_free(tls->ssl);
+ SSL_CTX_free(tls->ctx);
+ free(tls);
+ c->tls = NULL;
+}
+
+size_t mg_tls_pending(struct mg_connection *c) {
+ struct mg_tls *tls = (struct mg_tls *) c->tls;
+ return tls == NULL ? 0 : (size_t) SSL_pending(tls->ssl);
+}
+
+long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) {
+ struct mg_tls *tls = (struct mg_tls *) c->tls;
+ int n = SSL_read(tls->ssl, buf, (int) len);
+ if (n < 0 && mg_tls_err(tls, n) == 0) return MG_IO_WAIT;
+ if (n <= 0) return MG_IO_ERR;
+ return n;
+}
+
+long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) {
+ struct mg_tls *tls = (struct mg_tls *) c->tls;
+ int n = SSL_write(tls->ssl, buf, (int) len);
+ if (n < 0 && mg_tls_err(tls, n) == 0) return MG_IO_WAIT;
+ if (n <= 0) return MG_IO_ERR;
+ return n;
+}
+#endif
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/url.c"
+#endif
+
+
+struct url {
+ size_t key, user, pass, host, port, uri, end;
+};
+
+int mg_url_is_ssl(const char *url) {
+ return strncmp(url, "wss:", 4) == 0 || strncmp(url, "https:", 6) == 0 ||
+ strncmp(url, "mqtts:", 6) == 0 || strncmp(url, "ssl:", 4) == 0 ||
+ strncmp(url, "tls:", 4) == 0;
+}
+
+static struct url urlparse(const char *url) {
+ size_t i;
+ struct url u;
+ memset(&u, 0, sizeof(u));
+ for (i = 0; url[i] != '\0'; i++) {
+ if (url[i] == '/' && i > 0 && u.host == 0 && url[i - 1] == '/') {
+ u.host = i + 1;
+ u.port = 0;
+ } else if (url[i] == ']') {
+ u.port = 0; // IPv6 URLs, like http://[::1]/bar
+ } else if (url[i] == ':' && u.port == 0 && u.uri == 0) {
+ u.port = i + 1;
+ } else if (url[i] == '@' && u.user == 0 && u.pass == 0 && u.uri == 0) {
+ u.user = u.host;
+ u.pass = u.port;
+ u.host = i + 1;
+ u.port = 0;
+ } else if (url[i] == '/' && u.host && u.uri == 0) {
+ u.uri = i;
+ }
+ }
+ u.end = i;
+#if 0
+ printf("[%s] %d %d %d %d %d\n", url, u.user, u.pass, u.host, u.port, u.uri);
+#endif
+ return u;
+}
+
+struct mg_str mg_url_host(const char *url) {
+ struct url u = urlparse(url);
+ size_t n = u.port ? u.port - u.host - 1
+ : u.uri ? u.uri - u.host
+ : u.end - u.host;
+ struct mg_str s = mg_str_n(url + u.host, n);
+ return s;
+}
+
+const char *mg_url_uri(const char *url) {
+ struct url u = urlparse(url);
+ return u.uri ? url + u.uri : "/";
+}
+
+unsigned short mg_url_port(const char *url) {
+ struct url u = urlparse(url);
+ unsigned short port = 0;
+ if (strncmp(url, "http:", 5) == 0 || strncmp(url, "ws:", 3) == 0) port = 80;
+ if (strncmp(url, "wss:", 4) == 0 || strncmp(url, "https:", 6) == 0)
+ port = 443;
+ if (strncmp(url, "mqtt:", 5) == 0) port = 1883;
+ if (strncmp(url, "mqtts:", 6) == 0) port = 8883;
+ if (u.port) port = (unsigned short) atoi(url + u.port);
+ return port;
+}
+
+struct mg_str mg_url_user(const char *url) {
+ struct url u = urlparse(url);
+ struct mg_str s = mg_str("");
+ if (u.user && (u.pass || u.host)) {
+ size_t n = u.pass ? u.pass - u.user - 1 : u.host - u.user - 1;
+ s = mg_str_n(url + u.user, n);
+ }
+ return s;
+}
+
+struct mg_str mg_url_pass(const char *url) {
+ struct url u = urlparse(url);
+ struct mg_str s = mg_str_n("", 0UL);
+ if (u.pass && u.host) {
+ size_t n = u.host - u.pass - 1;
+ s = mg_str_n(url + u.pass, n);
+ }
+ return s;
+}
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/util.c"
+#endif
+
+
+#if MG_ENABLE_CUSTOM_RANDOM
+#else
+void mg_random(void *buf, size_t len) {
+ bool done = false;
+ unsigned char *p = (unsigned char *) buf;
+#if MG_ARCH == MG_ARCH_ESP32
+ while (len--) *p++ = (unsigned char) (esp_random() & 255);
+ done = true;
+#elif MG_ARCH == MG_ARCH_WIN32
+#elif MG_ARCH == MG_ARCH_UNIX
+ FILE *fp = fopen("/dev/urandom", "rb");
+ if (fp != NULL) {
+ if (fread(buf, 1, len, fp) == len) done = true;
+ fclose(fp);
+ }
+#endif
+ // If everything above did not work, fallback to a pseudo random generator
+ while (!done && len--) *p++ = (unsigned char) (rand() & 255);
+}
+#endif
+
+char *mg_random_str(char *buf, size_t len) {
+ size_t i;
+ mg_random(buf, len);
+ for (i = 0; i < len; i++) {
+ uint8_t c = ((uint8_t *) buf)[i] % 62U;
+ buf[i] = i == len - 1 ? (char) '\0' // 0-terminate last byte
+ : c < 26 ? (char) ('a' + c) // lowercase
+ : c < 52 ? (char) ('A' + c - 26) // uppercase
+ : (char) ('0' + c - 52); // numeric
+ }
+ return buf;
+}
+
+uint32_t mg_ntohl(uint32_t net) {
+ uint8_t data[4] = {0, 0, 0, 0};
+ memcpy(&data, &net, sizeof(data));
+ return (((uint32_t) data[3]) << 0) | (((uint32_t) data[2]) << 8) |
+ (((uint32_t) data[1]) << 16) | (((uint32_t) data[0]) << 24);
+}
+
+uint16_t mg_ntohs(uint16_t net) {
+ uint8_t data[2] = {0, 0};
+ memcpy(&data, &net, sizeof(data));
+ return (uint16_t) ((uint16_t) data[1] | (((uint16_t) data[0]) << 8));
+}
+
+uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len) {
+ static const uint32_t crclut[16] = {
+ // table for polynomial 0xEDB88320 (reflected)
+ 0x00000000, 0x1DB71064, 0x3B6E20C8, 0x26D930AC, 0x76DC4190, 0x6B6B51F4,
+ 0x4DB26158, 0x5005713C, 0xEDB88320, 0xF00F9344, 0xD6D6A3E8, 0xCB61B38C,
+ 0x9B64C2B0, 0x86D3D2D4, 0xA00AE278, 0xBDBDF21C};
+ crc = ~crc;
+ while (len--) {
+ uint8_t byte = *(uint8_t *)buf++;
+ crc = crclut[(crc ^ byte) & 0x0F] ^ (crc >> 4);
+ crc = crclut[(crc ^ (byte >> 4)) & 0x0F] ^ (crc >> 4);
+ }
+ return ~crc;
+}
+
+static int isbyte(int n) {
+ return n >= 0 && n <= 255;
+}
+
+static int parse_net(const char *spec, uint32_t *net, uint32_t *mask) {
+ int n, a, b, c, d, slash = 32, len = 0;
+ if ((sscanf(spec, "%d.%d.%d.%d/%d%n", &a, &b, &c, &d, &slash, &n) == 5 ||
+ sscanf(spec, "%d.%d.%d.%d%n", &a, &b, &c, &d, &n) == 4) &&
+ isbyte(a) && isbyte(b) && isbyte(c) && isbyte(d) && slash >= 0 &&
+ slash < 33) {
+ len = n;
+ *net = ((uint32_t) a << 24) | ((uint32_t) b << 16) | ((uint32_t) c << 8) |
+ (uint32_t) d;
+ *mask = slash ? (uint32_t) (0xffffffffU << (32 - slash)) : (uint32_t) 0;
+ }
+ return len;
+}
+
+int mg_check_ip_acl(struct mg_str acl, uint32_t remote_ip) {
+ struct mg_str k, v;
+ int allowed = acl.len == 0 ? '+' : '-'; // If any ACL is set, deny by default
+ while (mg_commalist(&acl, &k, &v)) {
+ uint32_t net, mask;
+ if (k.ptr[0] != '+' && k.ptr[0] != '-') return -1;
+ if (parse_net(&k.ptr[1], &net, &mask) == 0) return -2;
+ if ((mg_ntohl(remote_ip) & mask) == net) allowed = k.ptr[0];
+ }
+ return allowed == '+';
+}
+
+#if MG_ENABLE_CUSTOM_MILLIS
+#else
+uint64_t mg_millis(void) {
+#if MG_ARCH == MG_ARCH_WIN32
+ return GetTickCount();
+#elif MG_ARCH == MG_ARCH_RP2040
+ return time_us_64() / 1000;
+#elif MG_ARCH == MG_ARCH_ESP32
+ return esp_timer_get_time() / 1000;
+#elif MG_ARCH == MG_ARCH_ESP8266 || MG_ARCH == MG_ARCH_FREERTOS
+ return xTaskGetTickCount() * portTICK_PERIOD_MS;
+#elif MG_ARCH == MG_ARCH_AZURERTOS
+ return tx_time_get() * (1000 /* MS per SEC */ / TX_TIMER_TICKS_PER_SECOND);
+#elif MG_ARCH == MG_ARCH_TIRTOS
+ return (uint64_t) Clock_getTicks();
+#elif MG_ARCH == MG_ARCH_ZEPHYR
+ return (uint64_t) k_uptime_get();
+#elif MG_ARCH == MG_ARCH_CMSIS_RTOS1
+ return (uint64_t)rt_time_get();
+#elif MG_ARCH == MG_ARCH_CMSIS_RTOS2
+ return (uint64_t)((osKernelGetTickCount() * 1000) / osKernelGetTickFreq());
+#elif MG_ARCH == MG_ARCH_UNIX && defined(__APPLE__)
+ // Apple CLOCK_MONOTONIC_RAW is equivalent to CLOCK_BOOTTIME on linux
+ // Apple CLOCK_UPTIME_RAW is equivalent to CLOCK_MONOTONIC_RAW on linux
+ return clock_gettime_nsec_np(CLOCK_UPTIME_RAW) / 1000000;
+#elif MG_ARCH == MG_ARCH_UNIX
+ struct timespec ts = {0, 0};
+ // See #1615 - prefer monotonic clock
+#if defined(CLOCK_MONOTONIC_RAW)
+ // Raw hardware-based time that is not subject to NTP adjustment
+ clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
+#elif defined(CLOCK_MONOTONIC)
+ // Affected by the incremental adjustments performed by adjtime and NTP
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+#else
+ // Affected by discontinuous jumps in the system time and by the incremental
+ // adjustments performed by adjtime and NTP
+ clock_gettime(CLOCK_REALTIME, &ts);
+#endif
+ return ((uint64_t) ts.tv_sec * 1000 + (uint64_t) ts.tv_nsec / 1000000);
+#elif defined(ARDUINO)
+ return (uint64_t) millis();
+#else
+ return (uint64_t) (time(NULL) * 1000);
+#endif
+}
+#endif
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/ws.c"
+#endif
+
+
+
+
+
+
+
+
+
+
+
+struct ws_msg {
+ uint8_t flags;
+ size_t header_len;
+ size_t data_len;
+};
+
+size_t mg_ws_vprintf(struct mg_connection *c, int op, const char *fmt,
+ va_list *ap) {
+ size_t len = c->send.len;
+ size_t n = mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, ap);
+ mg_ws_wrap(c, c->send.len - len, op);
+ return n;
+}
+
+size_t mg_ws_printf(struct mg_connection *c, int op, const char *fmt, ...) {
+ size_t len = 0;
+ va_list ap;
+ va_start(ap, fmt);
+ len = mg_ws_vprintf(c, op, fmt, &ap);
+ va_end(ap);
+ return len;
+}
+
+static void ws_handshake(struct mg_connection *c, const struct mg_str *wskey,
+ const struct mg_str *wsproto, const char *fmt,
+ va_list *ap) {
+ const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+ unsigned char sha[20], b64_sha[30];
+
+ mg_sha1_ctx sha_ctx;
+ mg_sha1_init(&sha_ctx);
+ mg_sha1_update(&sha_ctx, (unsigned char *) wskey->ptr, wskey->len);
+ mg_sha1_update(&sha_ctx, (unsigned char *) magic, 36);
+ mg_sha1_final(sha, &sha_ctx);
+ mg_base64_encode(sha, sizeof(sha), (char *) b64_sha);
+ mg_xprintf(mg_pfn_iobuf, &c->send,
+ "HTTP/1.1 101 Switching Protocols\r\n"
+ "Upgrade: websocket\r\n"
+ "Connection: Upgrade\r\n"
+ "Sec-WebSocket-Accept: %s\r\n",
+ b64_sha);
+ if (fmt != NULL) mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, ap);
+ if (wsproto != NULL) {
+ mg_printf(c, "Sec-WebSocket-Protocol: %.*s\r\n", (int) wsproto->len,
+ wsproto->ptr);
+ }
+ mg_send(c, "\r\n", 2);
+}
+
+static uint32_t be32(const uint8_t *p) {
+ return (((uint32_t) p[3]) << 0) | (((uint32_t) p[2]) << 8) |
+ (((uint32_t) p[1]) << 16) | (((uint32_t) p[0]) << 24);
+}
+
+static size_t ws_process(uint8_t *buf, size_t len, struct ws_msg *msg) {
+ size_t i, n = 0, mask_len = 0;
+ memset(msg, 0, sizeof(*msg));
+ if (len >= 2) {
+ n = buf[1] & 0x7f; // Frame length
+ mask_len = buf[1] & 128 ? 4 : 0; // last bit is a mask bit
+ msg->flags = buf[0];
+ if (n < 126 && len >= mask_len) {
+ msg->data_len = n;
+ msg->header_len = 2 + mask_len;
+ } else if (n == 126 && len >= 4 + mask_len) {
+ msg->header_len = 4 + mask_len;
+ msg->data_len = (((size_t) buf[2]) << 8) | buf[3];
+ } else if (len >= 10 + mask_len) {
+ msg->header_len = 10 + mask_len;
+ msg->data_len =
+ (size_t) (((uint64_t) be32(buf + 2) << 32) + be32(buf + 6));
+ }
+ }
+ // Sanity check, and integer overflow protection for the boundary check below
+ // data_len should not be larger than 1 Gb
+ if (msg->data_len > 1024 * 1024 * 1024) return 0;
+ if (msg->header_len + msg->data_len > len) return 0;
+ if (mask_len > 0) {
+ uint8_t *p = buf + msg->header_len, *m = p - mask_len;
+ for (i = 0; i < msg->data_len; i++) p[i] ^= m[i & 3];
+ }
+ return msg->header_len + msg->data_len;
+}
+
+static size_t mkhdr(size_t len, int op, bool is_client, uint8_t *buf) {
+ size_t n = 0;
+ buf[0] = (uint8_t) (op | 128);
+ if (len < 126) {
+ buf[1] = (unsigned char) len;
+ n = 2;
+ } else if (len < 65536) {
+ uint16_t tmp = mg_htons((uint16_t) len);
+ buf[1] = 126;
+ memcpy(&buf[2], &tmp, sizeof(tmp));
+ n = 4;
+ } else {
+ uint32_t tmp;
+ buf[1] = 127;
+ tmp = mg_htonl((uint32_t) (((uint64_t) len) >> 32));
+ memcpy(&buf[2], &tmp, sizeof(tmp));
+ tmp = mg_htonl((uint32_t) (len & 0xffffffffU));
+ memcpy(&buf[6], &tmp, sizeof(tmp));
+ n = 10;
+ }
+ if (is_client) {
+ buf[1] |= 1 << 7; // Set masking flag
+ mg_random(&buf[n], 4);
+ n += 4;
+ }
+ return n;
+}
+
+static void mg_ws_mask(struct mg_connection *c, size_t len) {
+ if (c->is_client && c->send.buf != NULL) {
+ size_t i;
+ uint8_t *p = c->send.buf + c->send.len - len, *mask = p - 4;
+ for (i = 0; i < len; i++) p[i] ^= mask[i & 3];
+ }
+}
+
+size_t mg_ws_send(struct mg_connection *c, const void *buf, size_t len,
+ int op) {
+ uint8_t header[14];
+ size_t header_len = mkhdr(len, op, c->is_client, header);
+ mg_send(c, header, header_len);
+ MG_VERBOSE(("WS out: %d [%.*s]", (int) len, (int) len, buf));
+ mg_send(c, buf, len);
+ mg_ws_mask(c, len);
+ return header_len + len;
+}
+
+static bool mg_ws_client_handshake(struct mg_connection *c) {
+ int n = mg_http_get_request_len(c->recv.buf, c->recv.len);
+ if (n < 0) {
+ mg_error(c, "not http"); // Some just, not an HTTP request
+ } else if (n > 0) {
+ if (n < 15 || memcmp(c->recv.buf + 9, "101", 3) != 0) {
+ mg_error(c, "handshake error");
+ } else {
+ struct mg_http_message hm;
+ mg_http_parse((char *) c->recv.buf, c->recv.len, &hm);
+ c->is_websocket = 1;
+ mg_call(c, MG_EV_WS_OPEN, &hm);
+ }
+ mg_iobuf_del(&c->recv, 0, (size_t) n);
+ } else {
+ return true; // Request is not yet received, quit event handler
+ }
+ return false; // Continue event handler
+}
+
+static void mg_ws_cb(struct mg_connection *c, int ev, void *ev_data,
+ void *fn_data) {
+ struct ws_msg msg;
+ size_t ofs = (size_t) c->pfn_data;
+
+ // assert(ofs < c->recv.len);
+ if (ev == MG_EV_READ) {
+ if (c->is_client && !c->is_websocket && mg_ws_client_handshake(c)) return;
+
+ while (ws_process(c->recv.buf + ofs, c->recv.len - ofs, &msg) > 0) {
+ char *s = (char *) c->recv.buf + ofs + msg.header_len;
+ struct mg_ws_message m = {{s, msg.data_len}, msg.flags};
+ size_t len = msg.header_len + msg.data_len;
+ uint8_t final = msg.flags & 128, op = msg.flags & 15;
+ // MG_VERBOSE ("fin %d op %d len %d [%.*s]", final, op,
+ // (int) m.data.len, (int) m.data.len, m.data.ptr));
+ switch (op) {
+ case WEBSOCKET_OP_CONTINUE:
+ mg_call(c, MG_EV_WS_CTL, &m);
+ break;
+ case WEBSOCKET_OP_PING:
+ MG_DEBUG(("%s", "WS PONG"));
+ mg_ws_send(c, s, msg.data_len, WEBSOCKET_OP_PONG);
+ mg_call(c, MG_EV_WS_CTL, &m);
+ break;
+ case WEBSOCKET_OP_PONG:
+ mg_call(c, MG_EV_WS_CTL, &m);
+ break;
+ case WEBSOCKET_OP_TEXT:
+ case WEBSOCKET_OP_BINARY:
+ if (final) mg_call(c, MG_EV_WS_MSG, &m);
+ break;
+ case WEBSOCKET_OP_CLOSE:
+ MG_DEBUG(("%lu WS CLOSE", c->id));
+ mg_call(c, MG_EV_WS_CTL, &m);
+ // Echo the payload of the received CLOSE message back to the sender
+ mg_ws_send(c, m.data.ptr, m.data.len, WEBSOCKET_OP_CLOSE);
+ c->is_draining = 1;
+ break;
+ default:
+ // Per RFC6455, close conn when an unknown op is recvd
+ mg_error(c, "unknown WS op %d", op);
+ break;
+ }
+
+ // Handle fragmented frames: strip header, keep in c->recv
+ if (final == 0 || op == 0) {
+ if (op) ofs++, len--, msg.header_len--; // First frame
+ mg_iobuf_del(&c->recv, ofs, msg.header_len); // Strip header
+ len -= msg.header_len;
+ ofs += len;
+ c->pfn_data = (void *) ofs;
+ // MG_INFO(("FRAG %d [%.*s]", (int) ofs, (int) ofs, c->recv.buf));
+ }
+ // Remove non-fragmented frame
+ if (final && op) mg_iobuf_del(&c->recv, ofs, len);
+ // Last chunk of the fragmented frame
+ if (final && !op) {
+ m.flags = c->recv.buf[0];
+ m.data = mg_str_n((char *) &c->recv.buf[1], (size_t) (ofs - 1));
+ mg_call(c, MG_EV_WS_MSG, &m);
+ mg_iobuf_del(&c->recv, 0, ofs);
+ ofs = 0;
+ c->pfn_data = NULL;
+ }
+ }
+ }
+ (void) fn_data;
+ (void) ev_data;
+}
+
+struct mg_connection *mg_ws_connect(struct mg_mgr *mgr, const char *url,
+ mg_event_handler_t fn, void *fn_data,
+ const char *fmt, ...) {
+ struct mg_connection *c = mg_connect(mgr, url, fn, fn_data);
+ if (c != NULL) {
+ char nonce[16], key[30];
+ struct mg_str host = mg_url_host(url);
+ mg_random(nonce, sizeof(nonce));
+ mg_base64_encode((unsigned char *) nonce, sizeof(nonce), key);
+ mg_xprintf(mg_pfn_iobuf, &c->send,
+ "GET %s HTTP/1.1\r\n"
+ "Upgrade: websocket\r\n"
+ "Host: %.*s\r\n"
+ "Connection: Upgrade\r\n"
+ "Sec-WebSocket-Version: 13\r\n"
+ "Sec-WebSocket-Key: %s\r\n",
+ mg_url_uri(url), (int) host.len, host.ptr, key);
+ if (fmt != NULL) {
+ va_list ap;
+ va_start(ap, fmt);
+ mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, &ap);
+ va_end(ap);
+ }
+ mg_xprintf(mg_pfn_iobuf, &c->send, "\r\n");
+ c->pfn = mg_ws_cb;
+ c->pfn_data = NULL;
+ }
+ return c;
+}
+
+void mg_ws_upgrade(struct mg_connection *c, struct mg_http_message *hm,
+ const char *fmt, ...) {
+ struct mg_str *wskey = mg_http_get_header(hm, "Sec-WebSocket-Key");
+ c->pfn = mg_ws_cb;
+ c->pfn_data = NULL;
+ if (wskey == NULL) {
+ mg_http_reply(c, 426, "", "WS upgrade expected\n");
+ c->is_draining = 1;
+ } else {
+ struct mg_str *wsproto = mg_http_get_header(hm, "Sec-WebSocket-Protocol");
+ va_list ap;
+ va_start(ap, fmt);
+ ws_handshake(c, wskey, wsproto, fmt, &ap);
+ va_end(ap);
+ c->is_websocket = 1;
+ c->is_resp = 0;
+ mg_call(c, MG_EV_WS_OPEN, hm);
+ }
+}
+
+size_t mg_ws_wrap(struct mg_connection *c, size_t len, int op) {
+ uint8_t header[14], *p;
+ size_t header_len = mkhdr(len, op, c->is_client, header);
+
+ // NOTE: order of operations is important!
+ mg_iobuf_add(&c->send, c->send.len, NULL, header_len);
+ p = &c->send.buf[c->send.len - len]; // p points to data
+ memmove(p, p - header_len, len); // Shift data
+ memcpy(p - header_len, header, header_len); // Prepend header
+ mg_ws_mask(c, len); // Mask data
+
+ return c->send.len;
+}
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/tcpip/driver_nxpimxrt1020.c"
+#endif
+
+
+/*
+ * Todo
+ * This driver doesn't support 10M line autoconfiguration yet.
+ * Packets aren't sent if the link negociated 10M line.
+ * todo: MAC back auto reconfiguration.
+ */
+
+#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_IMXRT1020)
+struct imx_rt1020_enet {
+volatile uint32_t RESERVED0, EIR, EIMR, RESERVED1, RDAR, TDAR, RESERVED2[3], ECR, RESERVED3[6], MMFR, MSCR, RESERVED4[7], MIBC, RESERVED5[7], RCR, RESERVED6[15], TCR, RESERVED7[7], PALR, PAUR, OPD, TXIC0, TXIC1, TXIC2, RESERVED8, RXIC0, RXIC1, RXIC2, RESERVED9[3], IAUR, IALR, GAUR, GALR, RESERVED10[7], TFWR, RESERVED11[14], RDSR, TDSR, MRBR[2], RSFL, RSEM, RAEM, RAFL, TSEM, TAEM, TAFL, TIPG, FTRL, RESERVED12[3], TACC, RACC, RESERVED13[15], RMON_T_PACKETS, RMON_T_BC_PKT, RMON_T_MC_PKT, RMON_T_CRC_ALIGN, RMON_T_UNDERSIZE, RMON_T_OVERSIZE, RMON_T_FRAG, RMON_T_JAB, RMON_T_COL, RMON_T_P64, RMON_T_P65TO127, RMON_T_P128TO255, RMON_T_P256TO511, RMON_T_P512TO1023, RMON_T_P1024TO2048, RMON_T_GTE2048, RMON_T_OCTETS, IEEE_T_DROP, IEEE_T_FRAME_OK, IEEE_T_1COL, IEEE_T_MCOL, IEEE_T_DEF, IEEE_T_LCOL, IEEE_T_EXCOL, IEEE_T_MACERR, IEEE_T_CSERR, IEEE_T_SQE, IEEE_T_FDXFC, IEEE_T_OCTETS_OK, RESERVED14[3], RMON_R_PACKETS, RMON_R_BC_PKT, RMON_R_MC_PKT, RMON_R_CRC_ALIGN, RMON_R_UNDERSIZE, RMON_R_OVERSIZE, RMON_R_FRAG, RMON_R_JAB, RESERVED15, RMON_R_P64, RMON_R_P65TO127, RMON_R_P128TO255, RMON_R_P256TO511, RMON_R_P512TO1023, RMON_R_P1024TO2047, RMON_R_GTE2048, RMON_R_OCTETS, IEEE_R_DROP, IEEE_R_FRAME_OK, IEEE_R_CRC, IEEE_R_ALIGN, IEEE_R_MACERR, IEEE_R_FDXFC, IEEE_R_OCTETS_OK, RESERVED16[71], ATCR, ATVR, ATOFF, ATPER, ATCOR, ATINC, ATSTMP, RESERVED17[122], TGSR, TCSR0, TCCR0, TCSR1, TCCR1, TCSR2, TCCR2, TCSR3;
+};
+
+#undef ENET
+#define ENET ((struct imx_rt1020_enet *) (uintptr_t) 0x402D8000u)
+
+#undef BIT
+#define BIT(x) ((uint32_t) 1 << (x))
+
+#define ENET_RXBUFF_SIZE 1536 // 1522 Buffer must be 64bits aligned
+#define ENET_TXBUFF_SIZE 1536 // 1522 hence set to 0x600 (1536)
+#define ENET_RXBD_NUM (4)
+#define ENET_TXBD_NUM (4)
+
+const uint32_t EIMR_RX_ERR = 0x2400000; // Intr mask RXF+EBERR
+
+void ETH_IRQHandler(void);
+static bool mg_tcpip_driver_imxrt1020_init(struct mg_tcpip_if *ifp);
+static void wait_phy_complete(void);
+static struct mg_tcpip_if *s_ifp; // MIP interface
+
+static size_t mg_tcpip_driver_imxrt1020_tx(const void *, size_t , struct mg_tcpip_if *);
+static bool mg_tcpip_driver_imxrt1020_up(struct mg_tcpip_if *ifp);
+
+enum { IMXRT1020_PHY_ADDR = 0x02, IMXRT1020_PHY_BCR = 0, IMXRT1020_PHY_BSR = 1 }; // PHY constants
+
+void delay(uint32_t);
+void delay (uint32_t di) {
+ volatile int dno = 0; // Prevent optimization
+ for (uint32_t i = 0; i < di; i++)
+ for (int j=0; j<20; j++) // PLLx20 (500 MHz/24MHz)
+ dno++;
+}
+
+static void wait_phy_complete(void) {
+ delay(0x00010000);
+ const uint32_t delay_max = 0x00100000;
+ uint32_t delay_cnt = 0;
+ while (!(ENET->EIR & BIT(23)) && (delay_cnt < delay_max))
+ {delay_cnt++;}
+ ENET->EIR |= BIT(23); // MII interrupt clear
+}
+
+static uint32_t imxrt1020_eth_read_phy(uint8_t addr, uint8_t reg) {
+ ENET->EIR |= BIT(23); // MII interrupt clear
+ uint32_t mask_phy_adr_reg = 0x1f; // 0b00011111: Ensure we write 5 bits (Phy address & register)
+ uint32_t phy_transaction = 0x00;
+ phy_transaction = (0x1 << 30) \
+ | (0x2 << 28) \
+ | ((uint32_t)(addr & mask_phy_adr_reg) << 23) \
+ | ((uint32_t)(reg & mask_phy_adr_reg) << 18) \
+ | (0x2 << 16);
+
+ ENET->MMFR = phy_transaction;
+ wait_phy_complete();
+
+ return (ENET->MMFR & 0x0000ffff);
+}
+
+static void imxrt1020_eth_write_phy(uint8_t addr, uint8_t reg, uint32_t val) {
+ ENET->EIR |= BIT(23); // MII interrupt clear
+ uint8_t mask_phy_adr_reg = 0x1f; // 0b00011111: Ensure we write 5 bits (Phy address & register)
+ uint32_t mask_phy_data = 0x0000ffff; // Ensure we write 16 bits (data)
+ addr &= mask_phy_adr_reg;
+ reg &= mask_phy_adr_reg;
+ val &= mask_phy_data;
+ uint32_t phy_transaction = 0x00;
+ phy_transaction = (uint32_t)(0x1 << 30) \
+ | (uint32_t)(0x1 << 28) \
+ | (uint32_t)(addr << 23) \
+ | (uint32_t)(reg << 18) \
+ | (uint32_t)(0x2 << 16) \
+ | (uint32_t)(val);
+ ENET->MMFR = phy_transaction;
+ wait_phy_complete();
+}
+
+// FEC RX/TX descriptors (Enhanced descriptor not enabled)
+// Descriptor buffer structure, little endian
+
+typedef struct enet_bd_struct_def
+{
+ uint16_t length; // Data length
+ uint16_t control; // Control and status
+ uint32_t *buffer; // Data ptr
+} enet_bd_struct_t;
+
+// Descriptor and buffer globals, in non-cached area, 64 bits aligned.
+
+__attribute__((section("NonCacheable,\"aw\",%nobits @"))) enet_bd_struct_t rx_buffer_descriptor[(ENET_RXBD_NUM)] __attribute__((aligned((64U))));
+__attribute__((section("NonCacheable,\"aw\",%nobits @"))) enet_bd_struct_t tx_buffer_descriptor[(ENET_TXBD_NUM)] __attribute__((aligned((64U))));
+
+uint8_t rx_data_buffer[(ENET_RXBD_NUM)][((unsigned int)(((ENET_RXBUFF_SIZE)) + (((64U))-1U)) & (unsigned int)(~(unsigned int)(((64U))-1U)))] __attribute__((aligned((64U))));
+uint8_t tx_data_buffer[(ENET_TXBD_NUM)][((unsigned int)(((ENET_TXBUFF_SIZE)) + (((64U))-1U)) & (unsigned int)(~(unsigned int)(((64U))-1U)))] __attribute__((aligned((64U))));
+
+// Initialise driver imx_rt1020
+
+// static bool mg_tcpip_driver_imxrt1020_init(uint8_t *mac, void *data) { // VO
+static bool mg_tcpip_driver_imxrt1020_init(struct mg_tcpip_if *ifp) {
+
+ struct mg_tcpip_driver_imxrt1020_data *d = (struct mg_tcpip_driver_imxrt1020_data *) ifp->driver_data;
+ s_ifp = ifp;
+
+ // ENET Reset, wait complete
+ ENET->ECR |= BIT(0);
+ while((ENET->ECR & BIT(0)) != 0) {}
+
+ // Re-latches the pin strapping pin values
+ ENET->ECR |= BIT(0);
+ while((ENET->ECR & BIT(0)) != 0) {}
+
+ // Setup MII/RMII MDC clock divider (<= 2.5MHz).
+ ENET->MSCR = 0x130; // HOLDTIME 2 clk, Preamble enable, MDC MII_Speed Div 0x30
+ imxrt1020_eth_write_phy(IMXRT1020_PHY_ADDR, IMXRT1020_PHY_BCR, 0x8000); // PHY W @0x00 D=0x8000 Soft reset
+ while (imxrt1020_eth_read_phy(IMXRT1020_PHY_ADDR, IMXRT1020_PHY_BSR) & BIT(15)) {delay(0x5000);} // Wait finished poll 10ms
+
+ // PHY: Start Link
+ {
+ imxrt1020_eth_write_phy(IMXRT1020_PHY_ADDR, IMXRT1020_PHY_BCR, 0x1200); // PHY W @0x00 D=0x1200 Autonego enable + start
+ imxrt1020_eth_write_phy(IMXRT1020_PHY_ADDR, 0x1f, 0x8180); // PHY W @0x1f D=0x8180 Ref clock 50 MHz at XI input
+
+ uint32_t bcr = imxrt1020_eth_read_phy(IMXRT1020_PHY_ADDR, IMXRT1020_PHY_BCR);
+ bcr &= ~BIT(10); // Isolation -> Normal
+ imxrt1020_eth_write_phy(IMXRT1020_PHY_ADDR, IMXRT1020_PHY_BCR, bcr);
+ }
+
+ // Disable ENET
+ ENET->ECR = 0x0; // Disable before configuration
+
+ // Configure ENET
+ ENET->RCR = 0x05ee0104; // #CRCFWD=0 (CRC kept in frame) + RMII + MII Enable
+
+ ENET->TCR = BIT(8) | BIT(2); // Addins (MAC address from PAUR+PALR) + Full duplex enable
+ //ENET->TFWR = BIT(8); // Store And Forward Enable, 64 bytes (minimize tx latency)
+
+ // Configure descriptors and buffers
+ // RX
+ for (int i = 0; i < ENET_RXBD_NUM; i++) {
+ // Wrap last descriptor buffer ptr
+ rx_buffer_descriptor[i].control = (BIT(15) | ((i<(ENET_RXBD_NUM-1))?0:BIT(13))); // E+(W*)
+ rx_buffer_descriptor[i].buffer = (uint32_t *)rx_data_buffer[i];
+ }
+
+ // TX
+ for (int i = 0; i < ENET_TXBD_NUM; i++) {
+ // Wrap last descriptor buffer ptr
+ tx_buffer_descriptor[i].control = ((i<(ENET_RXBD_NUM-1))?0:BIT(13)) | BIT(10); // (W*)+TC
+ tx_buffer_descriptor[i].buffer = (uint32_t *)tx_data_buffer[i];
+ }
+
+ // Continue ENET configuration
+ ENET->RDSR = (uint32_t)(uintptr_t)rx_buffer_descriptor;
+ ENET->TDSR = (uint32_t)(uintptr_t)tx_buffer_descriptor;
+ ENET->MRBR[0] = ENET_RXBUFF_SIZE; // Same size for RX/TX buffers
+
+ // MAC address filtering (bytes in reversed order)
+ ENET->PAUR = ((uint32_t) ifp->mac[4] << 24U) | (uint32_t) ifp->mac[5] << 16U;
+ ENET->PALR = (uint32_t) (ifp->mac[0] << 24U) | ((uint32_t) ifp->mac[1] << 16U) |
+ ((uint32_t) ifp->mac[2] << 8U) | ifp->mac[3];
+
+ // Init Hash tables (mac filtering)
+ ENET->IAUR = 0; // Unicast
+ ENET->IALR = 0;
+ ENET->GAUR = 0; // Multicast
+ ENET->GALR = 0;
+
+ // Set ENET Online
+ ENET->ECR |= BIT(8); // ENET Set Little-endian + (FEC buffer desc.)
+ ENET->ECR |= BIT(1); // Enable
+
+ // Set interrupt mask
+ ENET->EIMR = EIMR_RX_ERR;
+
+ // RX Descriptor activation
+ ENET->RDAR = BIT(24); // Activate Receive Descriptor
+ return true;
+}
+
+// Transmit frame
+static uint32_t s_rt1020_txno;
+
+static size_t mg_tcpip_driver_imxrt1020_tx(const void *buf, size_t len, struct mg_tcpip_if *ifp) {
+
+ if (len > sizeof(tx_data_buffer[ENET_TXBD_NUM])) {
+ // MG_ERROR(("Frame too big, %ld", (long) len));
+ len = 0; // Frame is too big
+ } else if ((tx_buffer_descriptor[s_rt1020_txno].control & BIT(15))) {
+ MG_ERROR(("No free descriptors"));
+ // printf("D0 %lx SR %lx\n", (long) s_txdesc[0][0], (long) ETH->DMASR);
+ len = 0; // All descriptors are busy, fail
+ } else {
+ memcpy(tx_data_buffer[s_rt1020_txno], buf, len); // Copy data
+ tx_buffer_descriptor[s_rt1020_txno].length = (uint16_t) len; // Set data len
+ tx_buffer_descriptor[s_rt1020_txno].control |= (uint16_t)(BIT(10)); // TC (transmit CRC)
+ // tx_buffer_descriptor[s_rt1020_txno].control &= (uint16_t)(BIT(14) | BIT(12)); // Own doesn't affect HW
+ tx_buffer_descriptor[s_rt1020_txno].control |= (uint16_t)(BIT(15) | BIT(11)); // R+L (ready+last)
+ ENET->TDAR = BIT(24); // Descriptor updated. Hand over to DMA.
+ // INFO
+ // Relevant Descriptor bits: 15(R) Ready
+ // 11(L) last in frame
+ // 10(TC) transmis CRC
+ // __DSB(); // ARM errata 838869 Cortex-M4, M4F, M7, M7F: "store immediate overlapping
+ // exception" return might vector to incorrect interrupt.
+ if (++s_rt1020_txno >= ENET_TXBD_NUM) s_rt1020_txno = 0;
+ }
+ (void) ifp;
+ return len;
+}
+
+// IRQ (RX)
+static uint32_t s_rt1020_rxno;
+
+void ENET_IRQHandler(void) {
+ ENET->EIMR = 0; // Mask interrupts.
+ uint32_t eir = ENET->EIR; // Read EIR
+ ENET->EIR = 0xffffffff; // Clear interrupts
+
+ if (eir & EIMR_RX_ERR) // Global mask used
+ {
+ if (rx_buffer_descriptor[s_rt1020_rxno].control & BIT(15)) {
+ ENET->EIMR = EIMR_RX_ERR; // Enable interrupts
+ return; // Empty? -> exit.
+ }
+ // Read inframes
+ else { // Frame received, loop
+ for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever
+ if (rx_buffer_descriptor[s_rt1020_rxno].control & BIT(15)) break; // exit when done
+ // Process if CRC OK and frame not truncated
+ if (!(rx_buffer_descriptor[s_rt1020_rxno].control & (BIT(2) | BIT(0)))) {
+ uint32_t len = (rx_buffer_descriptor[s_rt1020_rxno].length);
+ mg_tcpip_qwrite(rx_buffer_descriptor[s_rt1020_rxno].buffer, len > 4 ? len - 4 : len, s_ifp);
+ }
+ rx_buffer_descriptor[s_rt1020_rxno].control |= BIT(15); // Inform DMA RX is empty
+ if (++s_rt1020_rxno >= ENET_RXBD_NUM) s_rt1020_rxno = 0;
+ }
+ }
+ }
+ ENET->EIMR = EIMR_RX_ERR; // Enable interrupts
+}
+
+// Up/down status
+static bool mg_tcpip_driver_imxrt1020_up(struct mg_tcpip_if *ifp) {
+ uint32_t bsr = imxrt1020_eth_read_phy(IMXRT1020_PHY_ADDR, IMXRT1020_PHY_BSR);
+ (void) ifp;
+ return bsr & BIT(2) ? 1 : 0;
+}
+
+// API
+struct mg_tcpip_driver mg_tcpip_driver_imxrt1020 = {
+ mg_tcpip_driver_imxrt1020_init, mg_tcpip_driver_imxrt1020_tx, NULL,
+ mg_tcpip_driver_imxrt1020_up};
+
+#endif
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/tcpip/driver_stm32.c"
+#endif
+
+
+#if MG_ENABLE_TCPIP && MG_ENABLE_DRIVER_STM32
+struct stm32_eth {
+ volatile uint32_t MACCR, MACFFR, MACHTHR, MACHTLR, MACMIIAR, MACMIIDR, MACFCR,
+ MACVLANTR, RESERVED0[2], MACRWUFFR, MACPMTCSR, RESERVED1, MACDBGR, MACSR,
+ MACIMR, MACA0HR, MACA0LR, MACA1HR, MACA1LR, MACA2HR, MACA2LR, MACA3HR,
+ MACA3LR, RESERVED2[40], MMCCR, MMCRIR, MMCTIR, MMCRIMR, MMCTIMR,
+ RESERVED3[14], MMCTGFSCCR, MMCTGFMSCCR, RESERVED4[5], MMCTGFCR,
+ RESERVED5[10], MMCRFCECR, MMCRFAECR, RESERVED6[10], MMCRGUFCR,
+ RESERVED7[334], PTPTSCR, PTPSSIR, PTPTSHR, PTPTSLR, PTPTSHUR, PTPTSLUR,
+ PTPTSAR, PTPTTHR, PTPTTLR, RESERVED8, PTPTSSR, PTPPPSCR, RESERVED9[564],
+ DMABMR, DMATPDR, DMARPDR, DMARDLAR, DMATDLAR, DMASR, DMAOMR, DMAIER,
+ DMAMFBOCR, DMARSWTR, RESERVED10[8], DMACHTDR, DMACHRDR, DMACHTBAR,
+ DMACHRBAR;
+};
+#undef ETH
+#define ETH ((struct stm32_eth *) (uintptr_t) 0x40028000)
+
+#undef BIT
+#define BIT(x) ((uint32_t) 1 << (x))
+#define ETH_PKT_SIZE 1540 // Max frame size
+#define ETH_DESC_CNT 4 // Descriptors count
+#define ETH_DS 4 // Descriptor size (words)
+
+static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors
+static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors
+static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers
+static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers
+static uint8_t s_txno; // Current TX descriptor
+static uint8_t s_rxno; // Current RX descriptor
+
+static struct mg_tcpip_if *s_ifp; // MIP interface
+enum { PHY_ADDR = 0, PHY_BCR = 0, PHY_BSR = 1, PHY_CSCR = 31 };
+
+static uint32_t eth_read_phy(uint8_t addr, uint8_t reg) {
+ ETH->MACMIIAR &= (7 << 2);
+ ETH->MACMIIAR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6);
+ ETH->MACMIIAR |= BIT(0);
+ while (ETH->MACMIIAR & BIT(0)) (void) 0;
+ return ETH->MACMIIDR;
+}
+
+static void eth_write_phy(uint8_t addr, uint8_t reg, uint32_t val) {
+ ETH->MACMIIDR = val;
+ ETH->MACMIIAR &= (7 << 2);
+ ETH->MACMIIAR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6) | BIT(1);
+ ETH->MACMIIAR |= BIT(0);
+ while (ETH->MACMIIAR & BIT(0)) (void) 0;
+}
+
+static uint32_t get_hclk(void) {
+ struct rcc {
+ volatile uint32_t CR, PLLCFGR, CFGR;
+ } *rcc = (struct rcc *) 0x40023800;
+ uint32_t clk = 0, hsi = 16000000 /* 16 MHz */, hse = 8000000 /* 8MHz */;
+
+ if (rcc->CFGR & (1 << 2)) {
+ clk = hse;
+ } else if (rcc->CFGR & (1 << 3)) {
+ uint32_t vco, m, n, p;
+ m = (rcc->PLLCFGR & (0x3f << 0)) >> 0;
+ n = (rcc->PLLCFGR & (0x1ff << 6)) >> 6;
+ p = (((rcc->PLLCFGR & (3 << 16)) >> 16) + 1) * 2;
+ clk = (rcc->PLLCFGR & (1 << 22)) ? hse : hsi;
+ vco = (uint32_t) ((uint64_t) clk * n / m);
+ clk = vco / p;
+ } else {
+ clk = hsi;
+ }
+ uint32_t hpre = (rcc->CFGR & (15 << 4)) >> 4;
+ if (hpre < 8) return clk;
+
+ uint8_t ahbptab[8] = {1, 2, 3, 4, 6, 7, 8, 9}; // log2(div)
+ return ((uint32_t) clk) >> ahbptab[hpre - 8];
+}
+
+// Guess CR from HCLK. MDC clock is generated from HCLK (AHB); as per 802.3,
+// it must not exceed 2.5MHz As the AHB clock can be (and usually is) derived
+// from the HSI (internal RC), and it can go above specs, the datasheets
+// specify a range of frequencies and activate one of a series of dividers to
+// keep the MDC clock safely below 2.5MHz. We guess a divider setting based on
+// HCLK with a +5% drift. If the user uses a different clock from our
+// defaults, needs to set the macros on top Valid for STM32F74xxx/75xxx
+// (38.8.1) and STM32F42xxx/43xxx (33.8.1) (both 4.5% worst case drift)
+static int guess_mdc_cr(void) {
+ uint8_t crs[] = {2, 3, 0, 1, 4, 5}; // ETH->MACMIIAR::CR values
+ uint8_t div[] = {16, 26, 42, 62, 102, 124}; // Respective HCLK dividers
+ uint32_t hclk = get_hclk(); // Guess system HCLK
+ int result = -1; // Invalid CR value
+ if (hclk < 25000000) {
+ MG_ERROR(("HCLK too low"));
+ } else {
+ for (int i = 0; i < 6; i++) {
+ if (hclk / div[i] <= 2375000UL /* 2.5MHz - 5% */) {
+ result = crs[i];
+ break;
+ }
+ }
+ if (result < 0) MG_ERROR(("HCLK too high"));
+ }
+ MG_DEBUG(("HCLK: %u, CR: %d", hclk, result));
+ return result;
+}
+
+static bool mg_tcpip_driver_stm32_init(struct mg_tcpip_if *ifp) {
+ struct mg_tcpip_driver_stm32_data *d =
+ (struct mg_tcpip_driver_stm32_data *) ifp->driver_data;
+ s_ifp = ifp;
+
+ // Init RX descriptors
+ for (int i = 0; i < ETH_DESC_CNT; i++) {
+ s_rxdesc[i][0] = BIT(31); // Own
+ s_rxdesc[i][1] = sizeof(s_rxbuf[i]) | BIT(14); // 2nd address chained
+ s_rxdesc[i][2] = (uint32_t) (uintptr_t) s_rxbuf[i]; // Point to data buffer
+ s_rxdesc[i][3] =
+ (uint32_t) (uintptr_t) s_rxdesc[(i + 1) % ETH_DESC_CNT]; // Chain
+ }
+
+ // Init TX descriptors
+ for (int i = 0; i < ETH_DESC_CNT; i++) {
+ s_txdesc[i][2] = (uint32_t) (uintptr_t) s_txbuf[i]; // Buf pointer
+ s_txdesc[i][3] =
+ (uint32_t) (uintptr_t) s_txdesc[(i + 1) % ETH_DESC_CNT]; // Chain
+ }
+
+ ETH->DMABMR |= BIT(0); // Software reset
+ while ((ETH->DMABMR & BIT(0)) != 0) (void) 0; // Wait until done
+
+ // Set MDC clock divider. If user told us the value, use it. Otherwise, guess
+ int cr = (d == NULL || d->mdc_cr < 0) ? guess_mdc_cr() : d->mdc_cr;
+ ETH->MACMIIAR = ((uint32_t) cr & 7) << 2;
+
+ // NOTE(cpq): we do not use extended descriptor bit 7, and do not use
+ // hardware checksum. Therefore, descriptor size is 4, not 8
+ // ETH->DMABMR = BIT(13) | BIT(16) | BIT(22) | BIT(23) | BIT(25);
+ ETH->MACIMR = BIT(3) | BIT(9); // Mask timestamp & PMT IT
+ ETH->MACFCR = BIT(7); // Disable zero quarta pause
+ // ETH->MACFFR = BIT(31); // Receive all
+ eth_write_phy(PHY_ADDR, PHY_BCR, BIT(15)); // Reset PHY
+ eth_write_phy(PHY_ADDR, PHY_BCR, BIT(12)); // Set autonegotiation
+ ETH->DMARDLAR = (uint32_t) (uintptr_t) s_rxdesc; // RX descriptors
+ ETH->DMATDLAR = (uint32_t) (uintptr_t) s_txdesc; // RX descriptors
+ ETH->DMAIER = BIT(6) | BIT(16); // RIE, NISE
+ ETH->MACCR = BIT(2) | BIT(3) | BIT(11) | BIT(14); // RE, TE, Duplex, Fast
+ ETH->DMAOMR = BIT(1) | BIT(13) | BIT(21) | BIT(25); // SR, ST, TSF, RSF
+
+ // MAC address filtering
+ ETH->MACA0HR = ((uint32_t) ifp->mac[5] << 8U) | ifp->mac[4];
+ ETH->MACA0LR = (uint32_t) (ifp->mac[3] << 24) |
+ ((uint32_t) ifp->mac[2] << 16) |
+ ((uint32_t) ifp->mac[1] << 8) | ifp->mac[0];
+ return true;
+}
+
+static size_t mg_tcpip_driver_stm32_tx(const void *buf, size_t len,
+ struct mg_tcpip_if *ifp) {
+ if (len > sizeof(s_txbuf[s_txno])) {
+ MG_ERROR(("Frame too big, %ld", (long) len));
+ len = 0; // Frame is too big
+ } else if ((s_txdesc[s_txno][0] & BIT(31))) {
+ ifp->nerr++;
+ MG_ERROR(("No free descriptors"));
+ // printf("D0 %lx SR %lx\n", (long) s_txdesc[0][0], (long) ETH->DMASR);
+ len = 0; // All descriptors are busy, fail
+ } else {
+ memcpy(s_txbuf[s_txno], buf, len); // Copy data
+ s_txdesc[s_txno][1] = (uint32_t) len; // Set data len
+ s_txdesc[s_txno][0] = BIT(20) | BIT(28) | BIT(29) | BIT(30); // Chain,FS,LS
+ s_txdesc[s_txno][0] |= BIT(31); // Set OWN bit - let DMA take over
+ if (++s_txno >= ETH_DESC_CNT) s_txno = 0;
+ }
+ ETH->DMASR = BIT(2) | BIT(5); // Clear any prior TBUS/TUS
+ ETH->DMATPDR = 0; // and resume
+ return len;
+}
+
+static bool mg_tcpip_driver_stm32_up(struct mg_tcpip_if *ifp) {
+ uint32_t bsr = eth_read_phy(PHY_ADDR, PHY_BSR);
+ bool up = bsr & BIT(2) ? 1 : 0;
+ if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up
+ uint32_t scsr = eth_read_phy(PHY_ADDR, PHY_CSCR);
+ uint32_t maccr = ETH->MACCR | BIT(14) | BIT(11); // 100M, Full-duplex
+ if ((scsr & BIT(3)) == 0) maccr &= ~BIT(14); // 10M
+ if ((scsr & BIT(4)) == 0) maccr &= ~BIT(11); // Half-duplex
+ ETH->MACCR = maccr; // IRQ handler does not fiddle with this register
+ MG_DEBUG(("Link is %uM %s-duplex", maccr & BIT(14) ? 100 : 10,
+ maccr & BIT(11) ? "full" : "half"));
+ }
+ return up;
+}
+
+void ETH_IRQHandler(void);
+void ETH_IRQHandler(void) {
+ if (ETH->DMASR & BIT(6)) { // Frame received, loop
+ ETH->DMASR = BIT(16) | BIT(6); // Clear flag
+ for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever
+ if (s_rxdesc[s_rxno][0] & BIT(31)) break; // exit when done
+ if (((s_rxdesc[s_rxno][0] & (BIT(8) | BIT(9))) == (BIT(8) | BIT(9))) &&
+ !(s_rxdesc[s_rxno][0] & BIT(15))) { // skip partial/errored frames
+ uint32_t len = ((s_rxdesc[s_rxno][0] >> 16) & (BIT(14) - 1));
+ // printf("%lx %lu %lx %.8lx\n", s_rxno, len, s_rxdesc[s_rxno][0],
+ // ETH->DMASR);
+ mg_tcpip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp);
+ }
+ s_rxdesc[s_rxno][0] = BIT(31);
+ if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
+ }
+ }
+ ETH->DMASR = BIT(7); // Clear possible RBUS while processing
+ ETH->DMARPDR = 0; // and resume RX
+}
+
+struct mg_tcpip_driver mg_tcpip_driver_stm32 = {mg_tcpip_driver_stm32_init,
+ mg_tcpip_driver_stm32_tx, NULL,
+ mg_tcpip_driver_stm32_up};
+#endif
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/tcpip/driver_stm32h.c"
+#endif
+
+
+#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_STM32H) && \
+ MG_ENABLE_DRIVER_STM32H
+struct stm32h_eth {
+ volatile uint32_t MACCR, MACECR, MACPFR, MACWTR, MACHT0R, MACHT1R,
+ RESERVED1[14], MACVTR, RESERVED2, MACVHTR, RESERVED3, MACVIR, MACIVIR,
+ RESERVED4[2], MACTFCR, RESERVED5[7], MACRFCR, RESERVED6[7], MACISR,
+ MACIER, MACRXTXSR, RESERVED7, MACPCSR, MACRWKPFR, RESERVED8[2], MACLCSR,
+ MACLTCR, MACLETR, MAC1USTCR, RESERVED9[12], MACVR, MACDR, RESERVED10,
+ MACHWF0R, MACHWF1R, MACHWF2R, RESERVED11[54], MACMDIOAR, MACMDIODR,
+ RESERVED12[2], MACARPAR, RESERVED13[59], MACA0HR, MACA0LR, MACA1HR,
+ MACA1LR, MACA2HR, MACA2LR, MACA3HR, MACA3LR, RESERVED14[248], MMCCR,
+ MMCRIR, MMCTIR, MMCRIMR, MMCTIMR, RESERVED15[14], MMCTSCGPR, MMCTMCGPR,
+ RESERVED16[5], MMCTPCGR, RESERVED17[10], MMCRCRCEPR, MMCRAEPR,
+ RESERVED18[10], MMCRUPGR, RESERVED19[9], MMCTLPIMSTR, MMCTLPITCR,
+ MMCRLPIMSTR, MMCRLPITCR, RESERVED20[65], MACL3L4C0R, MACL4A0R,
+ RESERVED21[2], MACL3A0R0R, MACL3A1R0R, MACL3A2R0R, MACL3A3R0R,
+ RESERVED22[4], MACL3L4C1R, MACL4A1R, RESERVED23[2], MACL3A0R1R,
+ MACL3A1R1R, MACL3A2R1R, MACL3A3R1R, RESERVED24[108], MACTSCR, MACSSIR,
+ MACSTSR, MACSTNR, MACSTSUR, MACSTNUR, MACTSAR, RESERVED25, MACTSSR,
+ RESERVED26[3], MACTTSSNR, MACTTSSSR, RESERVED27[2], MACACR, RESERVED28,
+ MACATSNR, MACATSSR, MACTSIACR, MACTSEACR, MACTSICNR, MACTSECNR,
+ RESERVED29[4], MACPPSCR, RESERVED30[3], MACPPSTTSR, MACPPSTTNR, MACPPSIR,
+ MACPPSWR, RESERVED31[12], MACPOCR, MACSPI0R, MACSPI1R, MACSPI2R, MACLMIR,
+ RESERVED32[11], MTLOMR, RESERVED33[7], MTLISR, RESERVED34[55], MTLTQOMR,
+ MTLTQUR, MTLTQDR, RESERVED35[8], MTLQICSR, MTLRQOMR, MTLRQMPOCR, MTLRQDR,
+ RESERVED36[177], DMAMR, DMASBMR, DMAISR, DMADSR, RESERVED37[60], DMACCR,
+ DMACTCR, DMACRCR, RESERVED38[2], DMACTDLAR, RESERVED39, DMACRDLAR,
+ DMACTDTPR, RESERVED40, DMACRDTPR, DMACTDRLR, DMACRDRLR, DMACIER,
+ DMACRIWTR, DMACSFCSR, RESERVED41, DMACCATDR, RESERVED42, DMACCARDR,
+ RESERVED43, DMACCATBR, RESERVED44, DMACCARBR, DMACSR, RESERVED45[2],
+ DMACMFCR;
+};
+#undef ETH
+#define ETH \
+ ((struct stm32h_eth *) (uintptr_t) (0x40000000UL + 0x00020000UL + 0x8000UL))
+
+#undef BIT
+#define BIT(x) ((uint32_t) 1 << (x))
+#define ETH_PKT_SIZE 1540 // Max frame size
+#define ETH_DESC_CNT 4 // Descriptors count
+#define ETH_DS 4 // Descriptor size (words)
+
+static volatile uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors
+static volatile uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors
+static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers
+static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers
+static struct mg_tcpip_if *s_ifp; // MIP interface
+enum {
+ PHY_ADDR = 0,
+ PHY_BCR = 0,
+ PHY_BSR = 1,
+ PHY_CSCR = 31
+}; // PHY constants
+
+static uint32_t eth_read_phy(uint8_t addr, uint8_t reg) {
+ ETH->MACMDIOAR &= (0xF << 8);
+ ETH->MACMDIOAR |= ((uint32_t) addr << 21) | ((uint32_t) reg << 16) | 3 << 2;
+ ETH->MACMDIOAR |= BIT(0);
+ while (ETH->MACMDIOAR & BIT(0)) (void) 0;
+ return ETH->MACMDIODR;
+}
+
+static void eth_write_phy(uint8_t addr, uint8_t reg, uint32_t val) {
+ ETH->MACMDIODR = val;
+ ETH->MACMDIOAR &= (0xF << 8);
+ ETH->MACMDIOAR |= ((uint32_t) addr << 21) | ((uint32_t) reg << 16) | 1 << 2;
+ ETH->MACMDIOAR |= BIT(0);
+ while (ETH->MACMDIOAR & BIT(0)) (void) 0;
+}
+
+static uint32_t get_hclk(void) {
+ struct rcc {
+ volatile uint32_t CR, HSICFGR, CRRCR, CSICFGR, CFGR, RESERVED1, D1CFGR,
+ D2CFGR, D3CFGR, RESERVED2, PLLCKSELR, PLLCFGR, PLL1DIVR, PLL1FRACR,
+ PLL2DIVR, PLL2FRACR, PLL3DIVR, PLL3FRACR, RESERVED3, D1CCIPR, D2CCIP1R,
+ D2CCIP2R, D3CCIPR, RESERVED4, CIER, CIFR, CICR, RESERVED5, BDCR, CSR,
+ RESERVED6, AHB3RSTR, AHB1RSTR, AHB2RSTR, AHB4RSTR, APB3RSTR, APB1LRSTR,
+ APB1HRSTR, APB2RSTR, APB4RSTR, GCR, RESERVED8, D3AMR, RESERVED11[9],
+ RSR, AHB3ENR, AHB1ENR, AHB2ENR, AHB4ENR, APB3ENR, APB1LENR, APB1HENR,
+ APB2ENR, APB4ENR, RESERVED12, AHB3LPENR, AHB1LPENR, AHB2LPENR,
+ AHB4LPENR, APB3LPENR, APB1LLPENR, APB1HLPENR, APB2LPENR, APB4LPENR,
+ RESERVED13[4];
+ } *rcc = ((struct rcc *) (0x40000000 + 0x18020000 + 0x4400));
+ uint32_t clk = 0, hsi = 64000000 /* 64 MHz */, hse = 8000000 /* 8MHz */,
+ csi = 4000000 /* 4MHz */;
+ unsigned int sel = (rcc->CFGR & (7 << 3)) >> 3;
+
+ if (sel == 1) {
+ clk = csi;
+ } else if (sel == 2) {
+ clk = hse;
+ } else if (sel == 3) {
+ uint32_t vco, m, n, p;
+ unsigned int src = (rcc->PLLCKSELR & (3 << 0)) >> 0;
+ m = ((rcc->PLLCKSELR & (0x3F << 4)) >> 4);
+ n = ((rcc->PLL1DIVR & (0x1FF << 0)) >> 0) + 1 +
+ ((rcc->PLLCFGR & BIT(0)) ? 1 : 0); // round-up in fractional mode
+ p = ((rcc->PLL1DIVR & (0x7F << 9)) >> 9) + 1;
+ if (src == 1) {
+ clk = csi;
+ } else if (src == 2) {
+ clk = hse;
+ } else {
+ clk = hsi;
+ clk >>= ((rcc->CR & 3) >> 3);
+ }
+ vco = (uint32_t) ((uint64_t) clk * n / m);
+ clk = vco / p;
+ } else {
+ clk = hsi;
+ clk >>= ((rcc->CR & 3) >> 3);
+ }
+ const uint8_t cptab[12] = {1, 2, 3, 4, 6, 7, 8, 9}; // log2(div)
+ uint32_t d1cpre = (rcc->D1CFGR & (0x0F << 8)) >> 8;
+ if (d1cpre >= 8) clk >>= cptab[d1cpre - 8];
+ MG_DEBUG(("D1 CLK: %u", clk));
+ uint32_t hpre = (rcc->D1CFGR & (0x0F << 0)) >> 0;
+ if (hpre < 8) return clk;
+ return ((uint32_t) clk) >> cptab[hpre - 8];
+}
+
+// Guess CR from AHB1 clock. MDC clock is generated from the ETH peripheral
+// clock (AHB1); as per 802.3, it must not exceed 2. As the AHB clock can
+// be derived from HSI or CSI (internal RC) clocks, and those can go above
+// specs, the datasheets specify a range of frequencies and activate one of a
+// series of dividers to keep the MDC clock safely below 2.5MHz. We guess a
+// divider setting based on HCLK with some drift. If the user uses a different
+// clock from our defaults, needs to set the macros on top. Valid for
+// STM32H74xxx/75xxx (58.11.4)(4.5% worst case drift)(CSI clock has a 7.5 %
+// worst case drift @ max temp)
+static int guess_mdc_cr(void) {
+ const uint8_t crs[] = {2, 3, 0, 1, 4, 5}; // ETH->MACMDIOAR::CR values
+ const uint8_t div[] = {16, 26, 42, 62, 102, 124}; // Respective HCLK dividers
+ uint32_t hclk = get_hclk(); // Guess system HCLK
+ int result = -1; // Invalid CR value
+ for (int i = 0; i < 6; i++) {
+ if (hclk / div[i] <= 2375000UL /* 2.5MHz - 5% */) {
+ result = crs[i];
+ break;
+ }
+ }
+ if (result < 0) MG_ERROR(("HCLK too high"));
+ MG_DEBUG(("HCLK: %u, CR: %d", hclk, result));
+ return result;
+}
+
+static bool mg_tcpip_driver_stm32h_init(struct mg_tcpip_if *ifp) {
+ struct mg_tcpip_driver_stm32h_data *d =
+ (struct mg_tcpip_driver_stm32h_data *) ifp->driver_data;
+ s_ifp = ifp;
+
+ // Init RX descriptors
+ for (int i = 0; i < ETH_DESC_CNT; i++) {
+ s_rxdesc[i][0] = (uint32_t) (uintptr_t) s_rxbuf[i]; // Point to data buffer
+ s_rxdesc[i][3] = BIT(31) | BIT(30) | BIT(24); // OWN, IOC, BUF1V
+ }
+
+ // Init TX descriptors
+ for (int i = 0; i < ETH_DESC_CNT; i++) {
+ s_txdesc[i][0] = (uint32_t) (uintptr_t) s_txbuf[i]; // Buf pointer
+ }
+
+ ETH->DMAMR |= BIT(0); // Software reset
+ while ((ETH->DMAMR & BIT(0)) != 0) (void) 0; // Wait until done
+
+ // Set MDC clock divider. If user told us the value, use it. Otherwise, guess
+ int cr = (d == NULL || d->mdc_cr < 0) ? guess_mdc_cr() : d->mdc_cr;
+ ETH->MACMDIOAR = ((uint32_t) cr & 0xF) << 8;
+
+ // NOTE(scaprile): We do not use timing facilities so the DMA engine does not
+ // re-write buffer address
+ ETH->DMAMR = 0 << 16; // use interrupt mode 0 (58.8.1) (reset value)
+ ETH->DMASBMR |= BIT(12); // AAL NOTE(scaprile): is this actually needed
+ ETH->MACIER = 0; // Do not enable additional irq sources (reset value)
+ ETH->MACTFCR = BIT(7); // Disable zero-quanta pause
+ // ETH->MACPFR = BIT(31); // Receive all
+ eth_write_phy(PHY_ADDR, PHY_BCR, BIT(15)); // Reset PHY
+ eth_write_phy(PHY_ADDR, PHY_BCR, BIT(12)); // Set autonegotiation
+ ETH->DMACRDLAR =
+ (uint32_t) (uintptr_t) s_rxdesc; // RX descriptors start address
+ ETH->DMACRDRLR = ETH_DESC_CNT - 1; // ring length
+ ETH->DMACRDTPR =
+ (uint32_t) (uintptr_t) &s_rxdesc[ETH_DESC_CNT -
+ 1]; // last valid descriptor address
+ ETH->DMACTDLAR =
+ (uint32_t) (uintptr_t) s_txdesc; // TX descriptors start address
+ ETH->DMACTDRLR = ETH_DESC_CNT - 1; // ring length
+ ETH->DMACTDTPR =
+ (uint32_t) (uintptr_t) s_txdesc; // first available descriptor address
+ ETH->DMACCR = 0; // DSL = 0 (contiguous descriptor table) (reset value)
+ ETH->DMACIER = BIT(6) | BIT(15); // RIE, NIE
+ ETH->MACCR = BIT(0) | BIT(1) | BIT(13) | BIT(14) |
+ BIT(15); // RE, TE, Duplex, Fast, Reserved
+ ETH->MTLTQOMR |= BIT(1); // TSF
+ ETH->MTLRQOMR |= BIT(5); // RSF
+ ETH->DMACTCR |= BIT(0); // ST
+ ETH->DMACRCR |= BIT(0); // SR
+
+ // MAC address filtering
+ ETH->MACA0HR = ((uint32_t) ifp->mac[5] << 8U) | ifp->mac[4];
+ ETH->MACA0LR = (uint32_t) (ifp->mac[3] << 24) |
+ ((uint32_t) ifp->mac[2] << 16) |
+ ((uint32_t) ifp->mac[1] << 8) | ifp->mac[0];
+ return true;
+}
+
+static uint32_t s_txno;
+static size_t mg_tcpip_driver_stm32h_tx(const void *buf, size_t len,
+ struct mg_tcpip_if *ifp) {
+ if (len > sizeof(s_txbuf[s_txno])) {
+ MG_ERROR(("Frame too big, %ld", (long) len));
+ len = 0; // Frame is too big
+ } else if ((s_txdesc[s_txno][3] & BIT(31))) {
+ MG_ERROR(("No free descriptors: %u %08X %08X %08X", s_txno,
+ s_txdesc[s_txno][3], ETH->DMACSR, ETH->DMACTCR));
+ for (int i = 0; i < ETH_DESC_CNT; i++) MG_ERROR(("%08X", s_txdesc[i][3]));
+ len = 0; // All descriptors are busy, fail
+ } else {
+ memcpy(s_txbuf[s_txno], buf, len); // Copy data
+ s_txdesc[s_txno][2] = (uint32_t) len; // Set data len
+ s_txdesc[s_txno][3] = BIT(28) | BIT(29); // FD, LD
+ s_txdesc[s_txno][3] |= BIT(31); // Set OWN bit - let DMA take over
+ if (++s_txno >= ETH_DESC_CNT) s_txno = 0;
+ }
+ ETH->DMACSR |= BIT(2) | BIT(1); // Clear any prior TBU, TPS
+ ETH->DMACTDTPR = (uint32_t) (uintptr_t) &s_txdesc[s_txno]; // and resume
+ return len;
+ (void) ifp;
+}
+
+static bool mg_tcpip_driver_stm32h_up(struct mg_tcpip_if *ifp) {
+ uint32_t bsr = eth_read_phy(PHY_ADDR, PHY_BSR);
+ bool up = bsr & BIT(2) ? 1 : 0;
+ if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up
+ uint32_t scsr = eth_read_phy(PHY_ADDR, PHY_CSCR);
+ uint32_t maccr = ETH->MACCR | BIT(14) | BIT(13); // 100M, Full-duplex
+ if ((scsr & BIT(3)) == 0) maccr &= ~BIT(14); // 10M
+ if ((scsr & BIT(4)) == 0) maccr &= ~BIT(13); // Half-duplex
+ ETH->MACCR = maccr; // IRQ handler does not fiddle with this register
+ MG_DEBUG(("Link is %uM %s-duplex", maccr & BIT(14) ? 100 : 10,
+ maccr & BIT(13) ? "full" : "half"));
+ }
+ return up;
+}
+
+void ETH_IRQHandler(void);
+static uint32_t s_rxno;
+void ETH_IRQHandler(void) {
+ if (ETH->DMACSR & BIT(6)) { // Frame received, loop
+ ETH->DMACSR = BIT(15) | BIT(6); // Clear flag
+ for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever
+ if (s_rxdesc[s_rxno][3] & BIT(31)) break; // exit when done
+ if (((s_rxdesc[s_rxno][3] & (BIT(28) | BIT(29))) ==
+ (BIT(28) | BIT(29))) &&
+ !(s_rxdesc[s_rxno][3] & BIT(15))) { // skip partial/errored frames
+ uint32_t len = s_rxdesc[s_rxno][3] & (BIT(15) - 1);
+ // MG_DEBUG(("%lx %lu %lx %08lx", s_rxno, len, s_rxdesc[s_rxno][3],
+ // ETH->DMACSR));
+ mg_tcpip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp);
+ }
+ s_rxdesc[s_rxno][3] = BIT(31) | BIT(30) | BIT(24); // OWN, IOC, BUF1V
+ if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
+ }
+ }
+ ETH->DMACSR = BIT(7) | BIT(8); // Clear possible RBU RPS while processing
+ ETH->DMACRDTPR =
+ (uint32_t) (uintptr_t) &s_rxdesc[ETH_DESC_CNT - 1]; // and resume RX
+}
+
+struct mg_tcpip_driver mg_tcpip_driver_stm32h = {
+ mg_tcpip_driver_stm32h_init, mg_tcpip_driver_stm32h_tx, NULL,
+ mg_tcpip_driver_stm32h_up};
+#endif
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/tcpip/driver_tm4c.c"
+#endif
+
+
+#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_TM4C) && MG_ENABLE_DRIVER_TM4C
+struct tm4c_emac {
+ volatile uint32_t EMACCFG, EMACFRAMEFLTR, EMACHASHTBLH, EMACHASHTBLL,
+ EMACMIIADDR, EMACMIIDATA, EMACFLOWCTL, EMACVLANTG, RESERVED0, EMACSTATUS,
+ EMACRWUFF, EMACPMTCTLSTAT, RESERVED1[2], EMACRIS, EMACIM, EMACADDR0H,
+ EMACADDR0L, EMACADDR1H, EMACADDR1L, EMACADDR2H, EMACADDR2L, EMACADDR3H,
+ EMACADDR3L, RESERVED2[31], EMACWDOGTO, RESERVED3[8], EMACMMCCTRL,
+ EMACMMCRXRIS, EMACMMCTXRIS, EMACMMCRXIM, EMACMMCTXIM, RESERVED4,
+ EMACTXCNTGB, RESERVED5[12], EMACTXCNTSCOL, EMACTXCNTMCOL, RESERVED6[4],
+ EMACTXOCTCNTG, RESERVED7[6], EMACRXCNTGB, RESERVED8[4], EMACRXCNTCRCERR,
+ EMACRXCNTALGNERR, RESERVED9[10], EMACRXCNTGUNI, RESERVED10[239],
+ EMACVLNINCREP, EMACVLANHASH, RESERVED11[93], EMACTIMSTCTRL, EMACSUBSECINC,
+ EMACTIMSEC, EMACTIMNANO, EMACTIMSECU, EMACTIMNANOU, EMACTIMADD,
+ EMACTARGSEC, EMACTARGNANO, EMACHWORDSEC, EMACTIMSTAT, EMACPPSCTRL,
+ RESERVED12[12], EMACPPS0INTVL, EMACPPS0WIDTH, RESERVED13[294],
+ EMACDMABUSMOD, EMACTXPOLLD, EMACRXPOLLD, EMACRXDLADDR, EMACTXDLADDR,
+ EMACDMARIS, EMACDMAOPMODE, EMACDMAIM, EMACMFBOC, EMACRXINTWDT,
+ RESERVED14[8], EMACHOSTXDESC, EMACHOSRXDESC, EMACHOSTXBA, EMACHOSRXBA,
+ RESERVED15[218], EMACPP, EMACPC, EMACCC, RESERVED16, EMACEPHYRIS,
+ EMACEPHYIM, EMACEPHYIMSC;
+};
+#undef EMAC
+#define EMAC ((struct tm4c_emac *) (uintptr_t) 0x400EC000)
+
+#undef BIT
+#define BIT(x) ((uint32_t) 1 << (x))
+#define ETH_PKT_SIZE 1540 // Max frame size
+#define ETH_DESC_CNT 4 // Descriptors count
+#define ETH_DS 4 // Descriptor size (words)
+
+static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors
+static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors
+static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers
+static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers
+static struct mg_tcpip_if *s_ifp; // MIP interface
+enum {
+ EPHY_ADDR = 0,
+ EPHYBMCR = 0,
+ EPHYBMSR = 1,
+ EPHYSTS = 16
+}; // PHY constants
+
+static inline void tm4cspin(volatile uint32_t count) {
+ while (count--) (void) 0;
+}
+
+static uint32_t emac_read_phy(uint8_t addr, uint8_t reg) {
+ EMAC->EMACMIIADDR &= (0xf << 2);
+ EMAC->EMACMIIADDR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6);
+ EMAC->EMACMIIADDR |= BIT(0);
+ while (EMAC->EMACMIIADDR & BIT(0)) tm4cspin(1);
+ return EMAC->EMACMIIDATA;
+}
+
+static void emac_write_phy(uint8_t addr, uint8_t reg, uint32_t val) {
+ EMAC->EMACMIIDATA = val;
+ EMAC->EMACMIIADDR &= (0xf << 2);
+ EMAC->EMACMIIADDR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6) | BIT(1);
+ EMAC->EMACMIIADDR |= BIT(0);
+ while (EMAC->EMACMIIADDR & BIT(0)) tm4cspin(1);
+}
+
+static uint32_t get_sysclk(void) {
+ struct sysctl {
+ volatile uint32_t DONTCARE0[44], RSCLKCFG, DONTCARE1[43], PLLFREQ0,
+ PLLFREQ1;
+ } *sysctl = (struct sysctl *) 0x400FE000;
+ uint32_t clk = 0, piosc = 16000000 /* 16 MHz */, mosc = 25000000 /* 25MHz */;
+ if (sysctl->RSCLKCFG & (1 << 28)) { // USEPLL
+ uint32_t fin, vco, mdiv, n, q, psysdiv;
+ uint32_t pllsrc = (sysctl->RSCLKCFG & (0xf << 24)) >> 24;
+ if (pllsrc == 0) {
+ clk = piosc;
+ } else if (pllsrc == 3) {
+ clk = mosc;
+ } else {
+ MG_ERROR(("Unsupported clock source"));
+ }
+ q = (sysctl->PLLFREQ1 & (0x1f << 8)) >> 8;
+ n = (sysctl->PLLFREQ1 & (0x1f << 0)) >> 0;
+ fin = clk / ((q + 1) * (n + 1));
+ mdiv = (sysctl->PLLFREQ0 & (0x3ff << 0)) >>
+ 0; // mint + (mfrac / 1024); MFRAC not supported
+ psysdiv = (sysctl->RSCLKCFG & (0x3f << 0)) >> 0;
+ vco = (uint32_t) ((uint64_t) fin * mdiv);
+ return vco / (psysdiv + 1);
+ }
+ uint32_t oscsrc = (sysctl->RSCLKCFG & (0xf << 20)) >> 20;
+ if (oscsrc == 0) {
+ clk = piosc;
+ } else if (oscsrc == 3) {
+ clk = mosc;
+ } else {
+ MG_ERROR(("Unsupported clock source"));
+ }
+ uint32_t osysdiv = (sysctl->RSCLKCFG & (0xf << 16)) >> 16;
+ return clk / (osysdiv + 1);
+}
+
+// Guess CR from SYSCLK. MDC clock is generated from SYSCLK (AHB); as per
+// 802.3, it must not exceed 2.5MHz (also 20.4.2.6) As the AHB clock can be
+// derived from the PIOSC (internal RC), and it can go above specs, the
+// datasheets specify a range of frequencies and activate one of a series of
+// dividers to keep the MDC clock safely below 2.5MHz. We guess a divider
+// setting based on SYSCLK with a +5% drift. If the user uses a different clock
+// from our defaults, needs to set the macros on top Valid for TM4C129x (20.7)
+// (4.5% worst case drift)
+// The PHY receives the main oscillator (MOSC) (20.3.1)
+static int guess_mdc_cr(void) {
+ uint8_t crs[] = {2, 3, 0, 1}; // EMAC->MACMIIAR::CR values
+ uint8_t div[] = {16, 26, 42, 62}; // Respective HCLK dividers
+ uint32_t sysclk = get_sysclk(); // Guess system SYSCLK
+ int result = -1; // Invalid CR value
+ if (sysclk < 25000000) {
+ MG_ERROR(("SYSCLK too low"));
+ } else {
+ for (int i = 0; i < 4; i++) {
+ if (sysclk / div[i] <= 2375000UL /* 2.5MHz - 5% */) {
+ result = crs[i];
+ break;
+ }
+ }
+ if (result < 0) MG_ERROR(("SYSCLK too high"));
+ }
+ MG_DEBUG(("SYSCLK: %u, CR: %d", sysclk, result));
+ return result;
+}
+
+static bool mg_tcpip_driver_tm4c_init(struct mg_tcpip_if *ifp) {
+ struct mg_tcpip_driver_tm4c_data *d =
+ (struct mg_tcpip_driver_tm4c_data *) ifp->driver_data;
+ s_ifp = ifp;
+
+ // Init RX descriptors
+ for (int i = 0; i < ETH_DESC_CNT; i++) {
+ s_rxdesc[i][0] = BIT(31); // Own
+ s_rxdesc[i][1] = sizeof(s_rxbuf[i]) | BIT(14); // 2nd address chained
+ s_rxdesc[i][2] = (uint32_t) (uintptr_t) s_rxbuf[i]; // Point to data buffer
+ s_rxdesc[i][3] =
+ (uint32_t) (uintptr_t) s_rxdesc[(i + 1) % ETH_DESC_CNT]; // Chain
+ // MG_DEBUG(("%d %p", i, s_rxdesc[i]));
+ }
+
+ // Init TX descriptors
+ for (int i = 0; i < ETH_DESC_CNT; i++) {
+ s_txdesc[i][2] = (uint32_t) (uintptr_t) s_txbuf[i]; // Buf pointer
+ s_txdesc[i][3] =
+ (uint32_t) (uintptr_t) s_txdesc[(i + 1) % ETH_DESC_CNT]; // Chain
+ }
+
+ EMAC->EMACDMABUSMOD |= BIT(0); // Software reset
+ while ((EMAC->EMACDMABUSMOD & BIT(0)) != 0) tm4cspin(1); // Wait until done
+
+ // Set MDC clock divider. If user told us the value, use it. Otherwise, guess
+ int cr = (d == NULL || d->mdc_cr < 0) ? guess_mdc_cr() : d->mdc_cr;
+ EMAC->EMACMIIADDR = ((uint32_t) cr & 0xf) << 2;
+
+ // NOTE(cpq): we do not use extended descriptor bit 7, and do not use
+ // hardware checksum. Therefore, descriptor size is 4, not 8
+ // EMAC->EMACDMABUSMOD = BIT(13) | BIT(16) | BIT(22) | BIT(23) | BIT(25);
+ EMAC->EMACIM = BIT(3) | BIT(9); // Mask timestamp & PMT IT
+ EMAC->EMACFLOWCTL = BIT(7); // Disable zero-quanta pause
+ // EMAC->EMACFRAMEFLTR = BIT(31); // Receive all
+ // EMAC->EMACPC defaults to internal PHY (EPHY) in MMI mode
+ emac_write_phy(EPHY_ADDR, EPHYBMCR, BIT(15)); // Reset internal PHY (EPHY)
+ emac_write_phy(EPHY_ADDR, EPHYBMCR, BIT(12)); // Set autonegotiation
+ EMAC->EMACRXDLADDR = (uint32_t) (uintptr_t) s_rxdesc; // RX descriptors
+ EMAC->EMACTXDLADDR = (uint32_t) (uintptr_t) s_txdesc; // TX descriptors
+ EMAC->EMACDMAIM = BIT(6) | BIT(16); // RIE, NIE
+ EMAC->EMACCFG = BIT(2) | BIT(3) | BIT(11) | BIT(14); // RE, TE, Duplex, Fast
+ EMAC->EMACDMAOPMODE =
+ BIT(1) | BIT(13) | BIT(21) | BIT(25); // SR, ST, TSF, RSF
+ EMAC->EMACADDR0H = ((uint32_t) ifp->mac[5] << 8U) | ifp->mac[4];
+ EMAC->EMACADDR0L = (uint32_t) (ifp->mac[3] << 24) |
+ ((uint32_t) ifp->mac[2] << 16) |
+ ((uint32_t) ifp->mac[1] << 8) | ifp->mac[0];
+ // NOTE(scaprile) There are 3 additional slots for filtering, disabled by
+ // default. This also applies to the STM32 driver (at least for F7)
+ return true;
+}
+
+static uint32_t s_txno;
+static size_t mg_tcpip_driver_tm4c_tx(const void *buf, size_t len,
+ struct mg_tcpip_if *ifp) {
+ if (len > sizeof(s_txbuf[s_txno])) {
+ MG_ERROR(("Frame too big, %ld", (long) len));
+ len = 0; // fail
+ } else if ((s_txdesc[s_txno][0] & BIT(31))) {
+ MG_ERROR(("No descriptors available"));
+ // printf("D0 %lx SR %lx\n", (long) s_txdesc[0][0], (long)
+ // EMAC->EMACDMARIS);
+ len = 0; // fail
+ } else {
+ memcpy(s_txbuf[s_txno], buf, len); // Copy data
+ s_txdesc[s_txno][1] = (uint32_t) len; // Set data len
+ s_txdesc[s_txno][0] =
+ BIT(20) | BIT(28) | BIT(29) | BIT(30); // Chain,FS,LS,IC
+ s_txdesc[s_txno][0] |= BIT(31); // Set OWN bit - let DMA take over
+ if (++s_txno >= ETH_DESC_CNT) s_txno = 0;
+ }
+ EMAC->EMACDMARIS = BIT(2) | BIT(5); // Clear any prior TU/UNF
+ EMAC->EMACTXPOLLD = 0; // and resume
+ return len;
+ (void) ifp;
+}
+
+static bool mg_tcpip_driver_tm4c_up(struct mg_tcpip_if *ifp) {
+ uint32_t bmsr = emac_read_phy(EPHY_ADDR, EPHYBMSR);
+ bool up = (bmsr & BIT(2)) ? 1 : 0;
+ if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up
+ uint32_t sts = emac_read_phy(EPHY_ADDR, EPHYSTS);
+ uint32_t emaccfg = EMAC->EMACCFG | BIT(14) | BIT(11); // 100M, Full-duplex
+ if (sts & BIT(1)) emaccfg &= ~BIT(14); // 10M
+ if ((sts & BIT(2)) == 0) emaccfg &= ~BIT(11); // Half-duplex
+ EMAC->EMACCFG = emaccfg; // IRQ handler does not fiddle with this register
+ MG_DEBUG(("Link is %uM %s-duplex", emaccfg & BIT(14) ? 100 : 10,
+ emaccfg & BIT(11) ? "full" : "half"));
+ }
+ return up;
+}
+
+void EMAC0_IRQHandler(void);
+static uint32_t s_rxno;
+void EMAC0_IRQHandler(void) {
+ if (EMAC->EMACDMARIS & BIT(6)) { // Frame received, loop
+ EMAC->EMACDMARIS = BIT(16) | BIT(6); // Clear flag
+ for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever
+ if (s_rxdesc[s_rxno][0] & BIT(31)) break; // exit when done
+ if (((s_rxdesc[s_rxno][0] & (BIT(8) | BIT(9))) == (BIT(8) | BIT(9))) &&
+ !(s_rxdesc[s_rxno][0] & BIT(15))) { // skip partial/errored frames
+ uint32_t len = ((s_rxdesc[s_rxno][0] >> 16) & (BIT(14) - 1));
+ // printf("%lx %lu %lx %.8lx\n", s_rxno, len, s_rxdesc[s_rxno][0],
+ // EMAC->EMACDMARIS);
+ mg_tcpip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp);
+ }
+ s_rxdesc[s_rxno][0] = BIT(31);
+ if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
+ }
+ }
+ EMAC->EMACDMARIS = BIT(7); // Clear possible RU while processing
+ EMAC->EMACRXPOLLD = 0; // and resume RX
+}
+
+struct mg_tcpip_driver mg_tcpip_driver_tm4c = {mg_tcpip_driver_tm4c_init,
+ mg_tcpip_driver_tm4c_tx, NULL,
+ mg_tcpip_driver_tm4c_up};
+#endif
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/tcpip/driver_w5500.c"
+#endif
+
+
+#if MG_ENABLE_TCPIP
+
+enum { W5500_CR = 0, W5500_S0 = 1, W5500_TX0 = 2, W5500_RX0 = 3 };
+
+static void w5500_txn(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, bool wr,
+ void *buf, size_t len) {
+ uint8_t *p = (uint8_t *) buf;
+ uint8_t cmd[] = {(uint8_t) (addr >> 8), (uint8_t) (addr & 255),
+ (uint8_t) ((block << 3) | (wr ? 4 : 0))};
+ s->begin(s->spi);
+ for (size_t i = 0; i < sizeof(cmd); i++) s->txn(s->spi, cmd[i]);
+ for (size_t i = 0; i < len; i++) {
+ uint8_t r = s->txn(s->spi, p[i]);
+ if (!wr) p[i] = r;
+ }
+ s->end(s->spi);
+}
+
+// clang-format off
+static void w5500_wn(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, void *buf, size_t len) { w5500_txn(s, block, addr, true, buf, len); }
+static void w5500_w1(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, uint8_t val) { w5500_wn(s, block, addr, &val, 1); }
+static void w5500_w2(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, uint16_t val) { uint8_t buf[2] = {(uint8_t) (val >> 8), (uint8_t) (val & 255)}; w5500_wn(s, block, addr, buf, sizeof(buf)); }
+static void w5500_rn(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, void *buf, size_t len) { w5500_txn(s, block, addr, false, buf, len); }
+static uint8_t w5500_r1(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr) { uint8_t r = 0; w5500_rn(s, block, addr, &r, 1); return r; }
+static uint16_t w5500_r2(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr) { uint8_t buf[2] = {0, 0}; w5500_rn(s, block, addr, buf, sizeof(buf)); return (uint16_t) ((buf[0] << 8) | buf[1]); }
+// clang-format on
+
+static size_t w5500_rx(void *buf, size_t buflen, struct mg_tcpip_if *ifp) {
+ struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) ifp->driver_data;
+ uint16_t r = 0, n = 0, len = (uint16_t) buflen, n2; // Read recv len
+ while ((n2 = w5500_r2(s, W5500_S0, 0x26)) > n) n = n2; // Until it is stable
+ // printf("RSR: %d\n", (int) n);
+ if (n > 0) {
+ uint16_t ptr = w5500_r2(s, W5500_S0, 0x28); // Get read pointer
+ n = w5500_r2(s, W5500_RX0, ptr); // Read frame length
+ if (n <= len + 2 && n > 1) {
+ r = (uint16_t) (n - 2);
+ w5500_rn(s, W5500_RX0, (uint16_t) (ptr + 2), buf, r);
+ }
+ w5500_w2(s, W5500_S0, 0x28, (uint16_t) (ptr + n)); // Advance read pointer
+ w5500_w1(s, W5500_S0, 1, 0x40); // Sock0 CR -> RECV
+ // printf(" RX_RD: tot=%u n=%u r=%u\n", n2, n, r);
+ }
+ return r;
+}
+
+static size_t w5500_tx(const void *buf, size_t buflen, struct mg_tcpip_if *ifp) {
+ struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) ifp->driver_data;
+ uint16_t n = 0, len = (uint16_t) buflen;
+ while (n < len) n = w5500_r2(s, W5500_S0, 0x20); // Wait for space
+ uint16_t ptr = w5500_r2(s, W5500_S0, 0x24); // Get write pointer
+ w5500_wn(s, W5500_TX0, ptr, (void *) buf, len); // Write data
+ w5500_w2(s, W5500_S0, 0x24, (uint16_t) (ptr + len)); // Advance write pointer
+ w5500_w1(s, W5500_S0, 1, 0x20); // Sock0 CR -> SEND
+ for (int i = 0; i < 40; i++) {
+ uint8_t ir = w5500_r1(s, W5500_S0, 2); // Read S0 IR
+ if (ir == 0) continue;
+ // printf("IR %d, len=%d, free=%d, ptr %d\n", ir, (int) len, (int) n, ptr);
+ w5500_w1(s, W5500_S0, 2, ir); // Write S0 IR: clear it!
+ if (ir & 8) len = 0; // Timeout. Report error
+ if (ir & (16 | 8)) break; // Stop on SEND_OK or timeout
+ }
+ return len;
+}
+
+static bool w5500_init(struct mg_tcpip_if *ifp) {
+ struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) ifp->driver_data;
+ s->end(s->spi);
+ w5500_w1(s, W5500_CR, 0, 0x80); // Reset chip: CR -> 0x80
+ w5500_w1(s, W5500_CR, 0x2e, 0); // CR PHYCFGR -> reset
+ w5500_w1(s, W5500_CR, 0x2e, 0xf8); // CR PHYCFGR -> set
+ // w5500_wn(s, W5500_CR, 9, s->mac, 6); // Set source MAC
+ w5500_w1(s, W5500_S0, 0x1e, 16); // Sock0 RX buf size
+ w5500_w1(s, W5500_S0, 0x1f, 16); // Sock0 TX buf size
+ w5500_w1(s, W5500_S0, 0, 4); // Sock0 MR -> MACRAW
+ w5500_w1(s, W5500_S0, 1, 1); // Sock0 CR -> OPEN
+ return w5500_r1(s, W5500_S0, 3) == 0x42; // Sock0 SR == MACRAW
+}
+
+static bool w5500_up(struct mg_tcpip_if *ifp) {
+ struct mg_tcpip_spi *spi = (struct mg_tcpip_spi *) ifp->driver_data;
+ uint8_t phycfgr = w5500_r1(spi, W5500_CR, 0x2e);
+ return phycfgr & 1; // Bit 0 of PHYCFGR is LNK (0 - down, 1 - up)
+}
+
+struct mg_tcpip_driver mg_tcpip_driver_w5500 = {w5500_init, w5500_tx, w5500_rx, w5500_up};
+#endif
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/tcpip/tcpip.c"
+#endif
+
+
+#if MG_ENABLE_TCPIP
+
+#define MG_EPHEMERAL_PORT_BASE 32768
+#define PDIFF(a, b) ((size_t) (((char *) (b)) - ((char *) (a))))
+
+#ifndef MIP_TCP_KEEPALIVE_MS
+#define MIP_TCP_KEEPALIVE_MS 45000 // TCP keep-alive period, ms
+#endif
+
+#define MIP_TCP_ACK_MS 150 // Timeout for ACKing
+
+struct connstate {
+ uint32_t seq, ack; // TCP seq/ack counters
+ uint64_t timer; // TCP keep-alive / ACK timer
+ uint8_t mac[6]; // Peer MAC address
+ uint8_t ttype; // Timer type. 0: ack, 1: keep-alive
+#define MIP_TTYPE_KEEPALIVE 0 // Connection is idle for long, send keepalive
+#define MIP_TTYPE_ACK 1 // Peer sent us data, we have to ack it soon
+ uint8_t tmiss; // Number of keep-alive misses
+ struct mg_iobuf raw; // For TLS only. Incoming raw data
+};
+
+#pragma pack(push, 1)
+
+struct lcp {
+ uint8_t addr, ctrl, proto[2], code, id, len[2];
+};
+
+struct eth {
+ uint8_t dst[6]; // Destination MAC address
+ uint8_t src[6]; // Source MAC address
+ uint16_t type; // Ethernet type
+};
+
+struct ip {
+ uint8_t ver; // Version
+ uint8_t tos; // Unused
+ uint16_t len; // Length
+ uint16_t id; // Unused
+ uint16_t frag; // Fragmentation
+ uint8_t ttl; // Time to live
+ uint8_t proto; // Upper level protocol
+ uint16_t csum; // Checksum
+ uint32_t src; // Source IP
+ uint32_t dst; // Destination IP
+};
+
+struct ip6 {
+ uint8_t ver; // Version
+ uint8_t opts[3]; // Options
+ uint16_t len; // Length
+ uint8_t proto; // Upper level protocol
+ uint8_t ttl; // Time to live
+ uint8_t src[16]; // Source IP
+ uint8_t dst[16]; // Destination IP
+};
+
+struct icmp {
+ uint8_t type;
+ uint8_t code;
+ uint16_t csum;
+};
+
+struct arp {
+ uint16_t fmt; // Format of hardware address
+ uint16_t pro; // Format of protocol address
+ uint8_t hlen; // Length of hardware address
+ uint8_t plen; // Length of protocol address
+ uint16_t op; // Operation
+ uint8_t sha[6]; // Sender hardware address
+ uint32_t spa; // Sender protocol address
+ uint8_t tha[6]; // Target hardware address
+ uint32_t tpa; // Target protocol address
+};
+
+struct tcp {
+ uint16_t sport; // Source port
+ uint16_t dport; // Destination port
+ uint32_t seq; // Sequence number
+ uint32_t ack; // Acknowledgement number
+ uint8_t off; // Data offset
+ uint8_t flags; // TCP flags
+#define TH_FIN 0x01
+#define TH_SYN 0x02
+#define TH_RST 0x04
+#define TH_PUSH 0x08
+#define TH_ACK 0x10
+#define TH_URG 0x20
+#define TH_ECE 0x40
+#define TH_CWR 0x80
+ uint16_t win; // Window
+ uint16_t csum; // Checksum
+ uint16_t urp; // Urgent pointer
+};
+
+struct udp {
+ uint16_t sport; // Source port
+ uint16_t dport; // Destination port
+ uint16_t len; // UDP length
+ uint16_t csum; // UDP checksum
+};
+
+struct dhcp {
+ uint8_t op, htype, hlen, hops;
+ uint32_t xid;
+ uint16_t secs, flags;
+ uint32_t ciaddr, yiaddr, siaddr, giaddr;
+ uint8_t hwaddr[208];
+ uint32_t magic;
+ uint8_t options[32];
+};
+
+#pragma pack(pop)
+
+struct pkt {
+ struct mg_str raw; // Raw packet data
+ struct mg_str pay; // Payload data
+ struct eth *eth;
+ struct llc *llc;
+ struct arp *arp;
+ struct ip *ip;
+ struct ip6 *ip6;
+ struct icmp *icmp;
+ struct tcp *tcp;
+ struct udp *udp;
+ struct dhcp *dhcp;
+};
+
+static void mkpay(struct pkt *pkt, void *p) {
+ pkt->pay =
+ mg_str_n((char *) p, (size_t) (&pkt->raw.ptr[pkt->raw.len] - (char *) p));
+}
+
+static uint32_t csumup(uint32_t sum, const void *buf, size_t len) {
+ const uint8_t *p = (const uint8_t *) buf;
+ for (size_t i = 0; i < len; i++) sum += i & 1 ? p[i] : (uint32_t) (p[i] << 8);
+ return sum;
+}
+
+static uint16_t csumfin(uint32_t sum) {
+ while (sum >> 16) sum = (sum & 0xffff) + (sum >> 16);
+ return mg_htons(~sum & 0xffff);
+}
+
+static uint16_t ipcsum(const void *buf, size_t len) {
+ uint32_t sum = csumup(0, buf, len);
+ return csumfin(sum);
+}
+
+static size_t ether_output(struct mg_tcpip_if *ifp, size_t len) {
+ // size_t min = 64; // Pad short frames to 64 bytes (minimum Ethernet size)
+ // if (len < min) memset(ifp->tx.ptr + len, 0, min - len), len = min;
+ // mg_hexdump(ifp->tx.ptr, len);
+ size_t n = ifp->driver->tx(ifp->tx.ptr, len, ifp);
+ if (n == len) ifp->nsent++;
+ return n;
+}
+
+static void arp_ask(struct mg_tcpip_if *ifp, uint32_t ip) {
+ struct eth *eth = (struct eth *) ifp->tx.ptr;
+ struct arp *arp = (struct arp *) (eth + 1);
+ memset(eth->dst, 255, sizeof(eth->dst));
+ memcpy(eth->src, ifp->mac, sizeof(eth->src));
+ eth->type = mg_htons(0x806);
+ memset(arp, 0, sizeof(*arp));
+ arp->fmt = mg_htons(1), arp->pro = mg_htons(0x800), arp->hlen = 6,
+ arp->plen = 4;
+ arp->op = mg_htons(1), arp->tpa = ip, arp->spa = ifp->ip;
+ memcpy(arp->sha, ifp->mac, sizeof(arp->sha));
+ ether_output(ifp, PDIFF(eth, arp + 1));
+}
+
+static void onstatechange(struct mg_tcpip_if *ifp) {
+ if (ifp->state == MG_TCPIP_STATE_READY) {
+ MG_INFO(("READY, IP: %M", mg_print_ip4, &ifp->ip));
+ MG_INFO((" GW: %M", mg_print_ip4, &ifp->gw));
+ if (ifp->lease_expire > ifp->now) {
+ MG_INFO(
+ (" Lease: %lld sec", (ifp->lease_expire - ifp->now) / 1000));
+ }
+ arp_ask(ifp, ifp->gw);
+ } else if (ifp->state == MG_TCPIP_STATE_UP) {
+ MG_ERROR(("Link up"));
+ srand((unsigned int) mg_millis());
+ } else if (ifp->state == MG_TCPIP_STATE_DOWN) {
+ MG_ERROR(("Link down"));
+ }
+}
+
+static struct ip *tx_ip(struct mg_tcpip_if *ifp, uint8_t *mac_dst,
+ uint8_t proto, uint32_t ip_src, uint32_t ip_dst,
+ size_t plen) {
+ struct eth *eth = (struct eth *) ifp->tx.ptr;
+ struct ip *ip = (struct ip *) (eth + 1);
+ memcpy(eth->dst, mac_dst, sizeof(eth->dst));
+ memcpy(eth->src, ifp->mac, sizeof(eth->src)); // Use our MAC
+ eth->type = mg_htons(0x800);
+ memset(ip, 0, sizeof(*ip));
+ ip->ver = 0x45; // Version 4, header length 5 words
+ ip->frag = 0x40; // Don't fragment
+ ip->len = mg_htons((uint16_t) (sizeof(*ip) + plen));
+ ip->ttl = 64;
+ ip->proto = proto;
+ ip->src = ip_src;
+ ip->dst = ip_dst;
+ ip->csum = ipcsum(ip, sizeof(*ip));
+ return ip;
+}
+
+static void tx_udp(struct mg_tcpip_if *ifp, uint8_t *mac_dst, uint32_t ip_src,
+ uint16_t sport, uint32_t ip_dst, uint16_t dport,
+ const void *buf, size_t len) {
+ struct ip *ip =
+ tx_ip(ifp, mac_dst, 17, ip_src, ip_dst, len + sizeof(struct udp));
+ struct udp *udp = (struct udp *) (ip + 1);
+ // MG_DEBUG(("UDP XX LEN %d %d", (int) len, (int) ifp->tx.len));
+ udp->sport = sport;
+ udp->dport = dport;
+ udp->len = mg_htons((uint16_t) (sizeof(*udp) + len));
+ udp->csum = 0;
+ uint32_t cs = csumup(0, udp, sizeof(*udp));
+ cs = csumup(cs, buf, len);
+ cs = csumup(cs, &ip->src, sizeof(ip->src));
+ cs = csumup(cs, &ip->dst, sizeof(ip->dst));
+ cs += (uint32_t) (ip->proto + sizeof(*udp) + len);
+ udp->csum = csumfin(cs);
+ memmove(udp + 1, buf, len);
+ // MG_DEBUG(("UDP LEN %d %d", (int) len, (int) ifp->frame_len));
+ ether_output(ifp, sizeof(struct eth) + sizeof(*ip) + sizeof(*udp) + len);
+}
+
+static void tx_dhcp(struct mg_tcpip_if *ifp, uint8_t *mac_dst, uint32_t ip_src,
+ uint32_t ip_dst, uint8_t *opts, size_t optslen) {
+ struct dhcp dhcp = {1, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, {0}, 0, {0}};
+ dhcp.magic = mg_htonl(0x63825363);
+ memcpy(&dhcp.hwaddr, ifp->mac, sizeof(ifp->mac));
+ memcpy(&dhcp.xid, ifp->mac + 2, sizeof(dhcp.xid));
+ memcpy(&dhcp.options, opts, optslen);
+ tx_udp(ifp, mac_dst, ip_src, mg_htons(68), ip_dst, mg_htons(67), &dhcp,
+ sizeof(dhcp));
+}
+
+static void tx_dhcp_request(struct mg_tcpip_if *ifp, uint8_t *mac_dst,
+ uint32_t ip_src, uint32_t ip_dst) {
+ uint8_t opts[] = {
+ 53, 1, 3, // Type: DHCP request
+ 55, 2, 1, 3, // GW and mask
+ 12, 3, 'm', 'i', 'p', // Host name: "mip"
+ 54, 4, 0, 0, 0, 0, // DHCP server ID
+ 50, 4, 0, 0, 0, 0, // Requested IP
+ 255 // End of options
+ };
+ memcpy(opts + 14, &ip_dst, sizeof(ip_dst));
+ memcpy(opts + 20, &ip_src, sizeof(ip_src));
+ tx_dhcp(ifp, mac_dst, ip_src, ip_dst, opts, sizeof(opts));
+}
+
+static void tx_dhcp_discover(struct mg_tcpip_if *ifp) {
+ uint8_t mac[6] = {255, 255, 255, 255, 255, 255};
+ uint8_t opts[] = {
+ 53, 1, 1, // Type: DHCP discover
+ 55, 2, 1, 3, // Parameters: ip, mask
+ 255 // End of options
+ };
+ tx_dhcp(ifp, mac, 0, 0xffffffff, opts, sizeof(opts));
+ MG_DEBUG(("DHCP discover sent"));
+}
+
+static struct mg_connection *getpeer(struct mg_mgr *mgr, struct pkt *pkt,
+ bool lsn) {
+ struct mg_connection *c = NULL;
+ for (c = mgr->conns; c != NULL; c = c->next) {
+ if (c->is_udp && pkt->udp && c->loc.port == pkt->udp->dport) break;
+ if (!c->is_udp && pkt->tcp && c->loc.port == pkt->tcp->dport &&
+ lsn == c->is_listening && (lsn || c->rem.port == pkt->tcp->sport))
+ break;
+ }
+ return c;
+}
+
+static void rx_arp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
+ if (pkt->arp->op == mg_htons(1) && pkt->arp->tpa == ifp->ip) {
+ // ARP request. Make a response, then send
+ // MG_DEBUG(("ARP op %d %M: %M", mg_ntohs(pkt->arp->op), mg_print_ip4,
+ // &pkt->arp->spa, mg_print_ip4, &pkt->arp->tpa));
+ struct eth *eth = (struct eth *) ifp->tx.ptr;
+ struct arp *arp = (struct arp *) (eth + 1);
+ memcpy(eth->dst, pkt->eth->src, sizeof(eth->dst));
+ memcpy(eth->src, ifp->mac, sizeof(eth->src));
+ eth->type = mg_htons(0x806);
+ *arp = *pkt->arp;
+ arp->op = mg_htons(2);
+ memcpy(arp->tha, pkt->arp->sha, sizeof(pkt->arp->tha));
+ memcpy(arp->sha, ifp->mac, sizeof(pkt->arp->sha));
+ arp->tpa = pkt->arp->spa;
+ arp->spa = ifp->ip;
+ MG_DEBUG(("ARP: tell %M we're %M", mg_print_ip4, &arp->tpa, mg_print_ip4,
+ &ifp->ip));
+ ether_output(ifp, PDIFF(eth, arp + 1));
+ } else if (pkt->arp->op == mg_htons(2)) {
+ if (memcmp(pkt->arp->tha, ifp->mac, sizeof(pkt->arp->tha)) != 0) return;
+ if (pkt->arp->spa == ifp->gw) {
+ // Got response for the GW ARP request. Set ifp->gwmac
+ memcpy(ifp->gwmac, pkt->arp->sha, sizeof(ifp->gwmac));
+ } else {
+ struct mg_connection *c = getpeer(ifp->mgr, pkt, false);
+ if (c != NULL && c->is_arplooking) {
+ struct connstate *s = (struct connstate *) (c + 1);
+ memcpy(s->mac, pkt->arp->sha, sizeof(s->mac));
+ MG_DEBUG(("%lu ARP resolved %M -> %M", c->id, mg_print_ip4, &c->rem.ip,
+ mg_print_mac, s->mac));
+ c->is_arplooking = 0;
+ }
+ }
+ }
+}
+
+static void rx_icmp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
+ // MG_DEBUG(("ICMP %d", (int) len));
+ if (pkt->icmp->type == 8 && pkt->ip != NULL && pkt->ip->dst == ifp->ip) {
+ size_t hlen = sizeof(struct eth) + sizeof(struct ip) + sizeof(struct icmp);
+ size_t space = ifp->tx.len - hlen, plen = pkt->pay.len;
+ if (plen > space) plen = space;
+ struct ip *ip = tx_ip(ifp, pkt->eth->src, 1, ifp->ip, pkt->ip->src,
+ sizeof(struct icmp) + plen);
+ struct icmp *icmp = (struct icmp *) (ip + 1);
+ memset(icmp, 0, sizeof(*icmp)); // Set csum to 0
+ memcpy(icmp + 1, pkt->pay.ptr, plen); // Copy RX payload to TX
+ icmp->csum = ipcsum(icmp, sizeof(*icmp) + plen);
+ ether_output(ifp, hlen + plen);
+ }
+}
+
+static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) {
+ uint32_t ip = 0, gw = 0, mask = 0;
+ uint8_t *p = pkt->dhcp->options,
+ *end = (uint8_t *) &pkt->raw.ptr[pkt->raw.len];
+ if (end < (uint8_t *) (pkt->dhcp + 1)) return;
+ while (p + 1 < end && p[0] != 255) { // Parse options
+ if (p[0] == 1 && p[1] == sizeof(ifp->mask) && p + 6 < end) { // Mask
+ memcpy(&mask, p + 2, sizeof(mask));
+ } else if (p[0] == 3 && p[1] == sizeof(ifp->gw) && p + 6 < end) { // GW
+ memcpy(&gw, p + 2, sizeof(gw));
+ ip = pkt->dhcp->yiaddr;
+ } else if (p[0] == 51 && p[1] == 4 && p + 6 < end) { // Lease
+ uint32_t lease = 0;
+ memcpy(&lease, p + 2, sizeof(lease));
+ ifp->lease_expire = ifp->now + mg_ntohl(lease) * 1000;
+ }
+ p += p[1] + 2;
+ }
+ if (ip && mask && gw && ifp->ip == 0) {
+ memcpy(ifp->gwmac, pkt->eth->src, sizeof(ifp->gwmac));
+ ifp->ip = ip, ifp->gw = gw, ifp->mask = mask;
+ ifp->state = MG_TCPIP_STATE_READY;
+ onstatechange(ifp);
+ tx_dhcp_request(ifp, pkt->eth->src, ip, pkt->dhcp->siaddr);
+ uint64_t rand;
+ mg_random(&rand, sizeof(rand));
+ srand((unsigned int) (rand + mg_millis()));
+ }
+}
+
+// Simple DHCP server that assigns a next IP address: ifp->ip + 1
+static void rx_dhcp_server(struct mg_tcpip_if *ifp, struct pkt *pkt) {
+ uint8_t op = 0, *p = pkt->dhcp->options,
+ *end = (uint8_t *) &pkt->raw.ptr[pkt->raw.len];
+ if (end < (uint8_t *) (pkt->dhcp + 1)) return;
+ // struct dhcp *req = pkt->dhcp;
+ struct dhcp res = {2, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, {0}, 0, {0}};
+ res.yiaddr = ifp->ip;
+ ((uint8_t *) (&res.yiaddr))[3]++; // Offer our IP + 1
+ while (p + 1 < end && p[0] != 255) { // Parse options
+ if (p[0] == 53 && p[1] == 1 && p + 2 < end) { // Message type
+ op = p[2];
+ }
+ p += p[1] + 2;
+ }
+ if (op == 1 || op == 3) { // DHCP Discover or DHCP Request
+ uint8_t msg = op == 1 ? 2 : 5; // Message type: DHCP OFFER or DHCP ACK
+ uint8_t opts[] = {
+ 53, 1, msg, // Message type
+ 1, 4, 0, 0, 0, 0, // Subnet mask
+ 54, 4, 0, 0, 0, 0, // Server ID
+ 12, 3, 'm', 'i', 'p', // Host name: "mip"
+ 51, 4, 255, 255, 255, 255, // Lease time
+ 255 // End of options
+ };
+ memcpy(&res.hwaddr, pkt->dhcp->hwaddr, 6);
+ memcpy(opts + 5, &ifp->mask, sizeof(ifp->mask));
+ memcpy(opts + 11, &ifp->ip, sizeof(ifp->ip));
+ memcpy(&res.options, opts, sizeof(opts));
+ res.magic = pkt->dhcp->magic;
+ res.xid = pkt->dhcp->xid;
+ // memcpy(ifp->gwmac, pkt->eth->src, sizeof(ifp->gwmac));
+ tx_udp(ifp, pkt->eth->src, ifp->ip, mg_htons(67),
+ op == 1 ? ~0U : res.yiaddr, mg_htons(68), &res, sizeof(res));
+ }
+}
+
+static void rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
+ struct mg_connection *c = getpeer(ifp->mgr, pkt, true);
+ if (c == NULL) {
+ // No UDP listener on this port. Should send ICMP, but keep silent.
+ } else {
+ c->rem.port = pkt->udp->sport;
+ c->rem.ip = pkt->ip->src;
+ struct connstate *s = (struct connstate *) (c + 1);
+ memcpy(s->mac, pkt->eth->src, sizeof(s->mac));
+ if (c->recv.len >= MG_MAX_RECV_SIZE) {
+ mg_error(c, "max_recv_buf_size reached");
+ } else if (c->recv.size - c->recv.len < pkt->pay.len &&
+ !mg_iobuf_resize(&c->recv, c->recv.len + pkt->pay.len)) {
+ mg_error(c, "oom");
+ } else {
+ memcpy(&c->recv.buf[c->recv.len], pkt->pay.ptr, pkt->pay.len);
+ c->recv.len += pkt->pay.len;
+ mg_call(c, MG_EV_READ, &pkt->pay.len);
+ }
+ }
+}
+
+static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *dst_mac, uint32_t dst_ip,
+ uint8_t flags, uint16_t sport, uint16_t dport,
+ uint32_t seq, uint32_t ack, const void *buf, size_t len) {
+ struct ip *ip =
+ tx_ip(ifp, dst_mac, 6, ifp->ip, dst_ip, sizeof(struct tcp) + len);
+ struct tcp *tcp = (struct tcp *) (ip + 1);
+ memset(tcp, 0, sizeof(*tcp));
+ if (buf != NULL && len) memmove(tcp + 1, buf, len);
+ tcp->sport = sport;
+ tcp->dport = dport;
+ tcp->seq = seq;
+ tcp->ack = ack;
+ tcp->flags = flags;
+ tcp->win = mg_htons(8192);
+ tcp->off = (uint8_t) (sizeof(*tcp) / 4 << 4);
+ uint32_t cs = 0;
+ uint16_t n = (uint16_t) (sizeof(*tcp) + len);
+ uint8_t pseudo[] = {0, ip->proto, (uint8_t) (n >> 8), (uint8_t) (n & 255)};
+ cs = csumup(cs, tcp, n);
+ cs = csumup(cs, &ip->src, sizeof(ip->src));
+ cs = csumup(cs, &ip->dst, sizeof(ip->dst));
+ cs = csumup(cs, pseudo, sizeof(pseudo));
+ tcp->csum = csumfin(cs);
+ MG_DEBUG(("TCP %M:%hu -> %M:%hu fl %x len %u", mg_print_ip4, &ip->src,
+ mg_ntohs(tcp->sport), mg_print_ip4, &ip->dst, mg_ntohs(tcp->dport),
+ tcp->flags, (int) len));
+ return ether_output(ifp, PDIFF(ifp->tx.ptr, tcp + 1) + len);
+}
+
+static size_t tx_tcp_pkt(struct mg_tcpip_if *ifp, struct pkt *pkt,
+ uint8_t flags, uint32_t seq, const void *buf,
+ size_t len) {
+ uint32_t delta = (pkt->tcp->flags & (TH_SYN | TH_FIN)) ? 1 : 0;
+ return tx_tcp(ifp, pkt->eth->src, pkt->ip->src, flags, pkt->tcp->dport,
+ pkt->tcp->sport, seq, mg_htonl(mg_ntohl(pkt->tcp->seq) + delta),
+ buf, len);
+}
+
+static void settmout(struct mg_connection *c, uint8_t type) {
+ struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv;
+ struct connstate *s = (struct connstate *) (c + 1);
+ unsigned n = type == MIP_TTYPE_ACK ? MIP_TCP_ACK_MS : MIP_TCP_KEEPALIVE_MS;
+ s->timer = ifp->now + n;
+ s->ttype = type;
+ MG_VERBOSE(("%lu %d -> %llx", c->id, type, s->timer));
+}
+
+static struct mg_connection *accept_conn(struct mg_connection *lsn,
+ struct pkt *pkt) {
+ struct mg_connection *c = mg_alloc_conn(lsn->mgr);
+ if (c == NULL) {
+ MG_ERROR(("OOM"));
+ return NULL;
+ }
+ struct connstate *s = (struct connstate *) (c + 1);
+ s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq);
+ memcpy(s->mac, pkt->eth->src, sizeof(s->mac));
+ settmout(c, MIP_TTYPE_KEEPALIVE);
+ c->rem.ip = pkt->ip->src;
+ c->rem.port = pkt->tcp->sport;
+ MG_DEBUG(("%lu accepted %M", c->id, mg_print_ip_port, &c->rem));
+ LIST_ADD_HEAD(struct mg_connection, &lsn->mgr->conns, c);
+ c->is_accepted = 1;
+ c->is_hexdumping = lsn->is_hexdumping;
+ c->pfn = lsn->pfn;
+ c->loc = lsn->loc;
+ c->pfn_data = lsn->pfn_data;
+ c->fn = lsn->fn;
+ c->fn_data = lsn->fn_data;
+ mg_call(c, MG_EV_OPEN, NULL);
+ mg_call(c, MG_EV_ACCEPT, NULL);
+ return c;
+}
+
+long mg_io_send(struct mg_connection *c, const void *buf, size_t len) {
+ struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv;
+ struct connstate *s = (struct connstate *) (c + 1);
+ if (c->is_udp) {
+ size_t max_headers_len = 14 + 24 /* max IP */ + 8 /* UDP */;
+ if (len + max_headers_len > ifp->tx.len) len = ifp->tx.len - max_headers_len;
+ tx_udp(ifp, s->mac, ifp->ip, c->loc.port, c->rem.ip, c->rem.port, buf, len);
+ } else {
+ size_t max_headers_len = 14 + 24 /* max IP */ + 60 /* max TCP */;
+ if (len + max_headers_len > ifp->tx.len) len = ifp->tx.len - max_headers_len;
+ if (tx_tcp(ifp, s->mac, c->rem.ip, TH_PUSH | TH_ACK, c->loc.port, c->rem.port,
+ mg_htonl(s->seq), mg_htonl(s->ack), buf, len) > 0) {
+ s->seq += (uint32_t) len;
+ if (s->ttype == MIP_TTYPE_ACK) settmout(c, MIP_TTYPE_KEEPALIVE);
+ } else {
+ return MG_IO_ERR;
+ }
+ }
+ return (long) len;
+}
+
+long mg_io_recv(struct mg_connection *c, void *buf, size_t len) {
+ struct connstate *s = (struct connstate *) (c + 1);
+ if (s->raw.len == 0) return MG_IO_WAIT;
+ if (len > s->raw.len) len = s->raw.len;
+ memcpy(buf, s->raw.buf, len);
+ mg_iobuf_del(&s->raw, 0, len);
+ MG_DEBUG(("%lu", len));
+ return (long) len;
+}
+
+static void read_conn(struct mg_connection *c, struct pkt *pkt) {
+ struct connstate *s = (struct connstate *) (c + 1);
+ struct mg_iobuf *io = c->is_tls ? &s->raw : &c->recv;
+ uint32_t seq = mg_ntohl(pkt->tcp->seq);
+ s->raw.align = c->recv.align;
+ if (pkt->tcp->flags & TH_FIN) {
+ s->ack = mg_htonl(pkt->tcp->seq) + 1, s->seq = mg_htonl(pkt->tcp->ack);
+ c->is_closing = 1;
+ } else if (pkt->pay.len == 0) {
+ // TODO(cpq): handle this peer's ACK
+ } else if (seq != s->ack) {
+ uint32_t ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len);
+ if (s->ack == ack) {
+ MG_VERBOSE(("ignoring duplicate pkt"));
+ } else {
+ // TODO(cpq): peer sent us SEQ which we don't expect. Retransmit rather
+ // than close this connection
+ mg_error(c, "SEQ != ACK: %x %x %x", seq, s->ack, ack);
+ }
+ } else if (io->size - io->len < pkt->pay.len &&
+ !mg_iobuf_resize(io, io->len + pkt->pay.len)) {
+ mg_error(c, "oom");
+ } else {
+ // Copy TCP payload into the IO buffer. If the connection is plain text, we
+ // copy to c->recv. If the connection is TLS, this data is encrypted,
+ // therefore we copy that encrypted data to the s->raw iobuffer instead,
+ // and then call mg_tls_recv() to decrypt it. NOTE: mg_tls_recv() will
+ // call back mg_io_recv() which grabs raw data from s->raw
+ memcpy(&io->buf[io->len], pkt->pay.ptr, pkt->pay.len);
+ io->len += pkt->pay.len;
+
+ MG_DEBUG(("%lu SEQ %x -> %x", c->id, mg_htonl(pkt->tcp->seq), s->ack));
+ // Advance ACK counter
+ s->ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len);
+#if 0
+ // Send ACK immediately
+ MG_DEBUG((" imm ACK", c->id, mg_htonl(pkt->tcp->seq), s->ack));
+ tx_tcp((struct mg_tcpip_if *) c->mgr->priv, c->rem.ip, TH_ACK, c->loc.port,
+ c->rem.port, mg_htonl(s->seq), mg_htonl(s->ack), "", 0);
+#else
+ // if not already running, setup a timer to send an ACK later
+ if (s->ttype != MIP_TTYPE_ACK) settmout(c, MIP_TTYPE_ACK);
+#endif
+
+ if (c->is_tls) {
+ // TLS connection. Make room for decrypted data in c->recv
+ io = &c->recv;
+ if (io->size - io->len < pkt->pay.len &&
+ !mg_iobuf_resize(io, io->len + pkt->pay.len)) {
+ mg_error(c, "oom");
+ } else {
+ // Decrypt data directly into c->recv
+ long n = mg_tls_recv(c, &io->buf[io->len], io->size - io->len);
+ if (n == MG_IO_ERR) {
+ mg_error(c, "TLS recv error");
+ } else if (n > 0) {
+ // Decrypted successfully - trigger MG_EV_READ
+ io->len += (size_t) n;
+ mg_call(c, MG_EV_READ, &n);
+ }
+ }
+ } else {
+ // Plain text connection, data is already in c->recv, trigger MG_EV_READ
+ mg_call(c, MG_EV_READ, &pkt->pay.len);
+ }
+ }
+}
+
+static void rx_tcp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
+ struct mg_connection *c = getpeer(ifp->mgr, pkt, false);
+ struct connstate *s = c == NULL ? NULL : (struct connstate *) (c + 1);
+#if 0
+ MG_INFO(("%lu %hhu %d", c ? c->id : 0, pkt->tcp->flags, (int) pkt->pay.len));
+#endif
+ if (c != NULL && c->is_connecting && pkt->tcp->flags & (TH_SYN | TH_ACK)) {
+ s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq) + 1;
+ tx_tcp_pkt(ifp, pkt, TH_ACK, pkt->tcp->ack, NULL, 0);
+ c->is_connecting = 0; // Client connected
+ settmout(c, MIP_TTYPE_KEEPALIVE);
+ mg_call(c, MG_EV_CONNECT, NULL); // Let user know
+ } else if (c != NULL && c->is_connecting) {
+ tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0);
+ } else if (c != NULL && pkt->tcp->flags & TH_RST) {
+ mg_error(c, "peer RST"); // RFC-1122 4.2.2.13
+ } else if (c != NULL) {
+#if 0
+ MG_DEBUG(("%lu %d %M:%hu -> %M:%hu", c->id, (int) pkt->raw.len,
+ mg_print_ip4, &pkt->ip->src, mg_ntohs(pkt->tcp->sport),
+ mg_print_ip4, &pkt->ip->dst, mg_ntohs(pkt->tcp->dport)));
+ mg_hexdump(pkt->pay.buf, pkt->pay.len);
+#endif
+ s->tmiss = 0; // Reset missed keep-alive counter
+ if (s->ttype == MIP_TTYPE_KEEPALIVE) // Advance keep-alive timer
+ settmout(c,
+ MIP_TTYPE_KEEPALIVE); // unless a former ACK timeout is pending
+ read_conn(c, pkt); // Override timer with ACK timeout if needed
+ } else if ((c = getpeer(ifp->mgr, pkt, true)) == NULL) {
+ tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0);
+ } else if (pkt->tcp->flags & TH_RST) {
+ if (c->is_accepted) mg_error(c, "peer RST"); // RFC-1122 4.2.2.13
+ // ignore RST if not connected
+ } else if (pkt->tcp->flags & TH_SYN) {
+ // Use peer's source port as ISN, in order to recognise the handshake
+ uint32_t isn = mg_htonl((uint32_t) mg_ntohs(pkt->tcp->sport));
+ tx_tcp_pkt(ifp, pkt, TH_SYN | TH_ACK, isn, NULL, 0);
+ } else if (pkt->tcp->flags & TH_FIN) {
+ tx_tcp_pkt(ifp, pkt, TH_FIN | TH_ACK, pkt->tcp->ack, NULL, 0);
+ } else if (mg_htonl(pkt->tcp->ack) == mg_htons(pkt->tcp->sport) + 1U) {
+ accept_conn(c, pkt);
+ } else if (!c->is_accepted) { // no peer
+ tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0);
+ } else {
+ // MG_DEBUG(("dropped silently.."));
+ }
+}
+
+static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) {
+ if (pkt->ip->proto == 1) {
+ pkt->icmp = (struct icmp *) (pkt->ip + 1);
+ if (pkt->pay.len < sizeof(*pkt->icmp)) return;
+ mkpay(pkt, pkt->icmp + 1);
+ rx_icmp(ifp, pkt);
+ } else if (pkt->ip->proto == 17) {
+ pkt->udp = (struct udp *) (pkt->ip + 1);
+ if (pkt->pay.len < sizeof(*pkt->udp)) return;
+ mkpay(pkt, pkt->udp + 1);
+ MG_DEBUG(("UDP %M:%hu -> %M:%hu len %u", mg_print_ip4, &pkt->ip->src,
+ mg_ntohs(pkt->udp->sport), mg_print_ip4, &pkt->ip->dst,
+ mg_ntohs(pkt->udp->dport), (int) pkt->pay.len));
+ if (pkt->udp->dport == mg_htons(68)) {
+ pkt->dhcp = (struct dhcp *) (pkt->udp + 1);
+ mkpay(pkt, pkt->dhcp + 1);
+ rx_dhcp_client(ifp, pkt);
+ } else if (ifp->enable_dhcp_server && pkt->udp->dport == mg_htons(67)) {
+ pkt->dhcp = (struct dhcp *) (pkt->udp + 1);
+ mkpay(pkt, pkt->dhcp + 1);
+ rx_dhcp_server(ifp, pkt);
+ } else {
+ rx_udp(ifp, pkt);
+ }
+ } else if (pkt->ip->proto == 6) {
+ pkt->tcp = (struct tcp *) (pkt->ip + 1);
+ if (pkt->pay.len < sizeof(*pkt->tcp)) return;
+ mkpay(pkt, pkt->tcp + 1);
+ uint16_t iplen = mg_ntohs(pkt->ip->len);
+ uint16_t off = (uint16_t) (sizeof(*pkt->ip) + ((pkt->tcp->off >> 4) * 4U));
+ if (iplen >= off) pkt->pay.len = (size_t) (iplen - off);
+ MG_DEBUG(("TCP %M:%hu -> %M:%hu len %u", mg_print_ip4, &pkt->ip->src,
+ mg_ntohs(pkt->tcp->sport), mg_print_ip4, &pkt->ip->dst,
+ mg_ntohs(pkt->tcp->dport), (int) pkt->pay.len));
+ rx_tcp(ifp, pkt);
+ }
+}
+
+static void rx_ip6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
+ // MG_DEBUG(("IP %d", (int) len));
+ if (pkt->ip6->proto == 1 || pkt->ip6->proto == 58) {
+ pkt->icmp = (struct icmp *) (pkt->ip6 + 1);
+ if (pkt->pay.len < sizeof(*pkt->icmp)) return;
+ mkpay(pkt, pkt->icmp + 1);
+ rx_icmp(ifp, pkt);
+ } else if (pkt->ip6->proto == 17) {
+ pkt->udp = (struct udp *) (pkt->ip6 + 1);
+ if (pkt->pay.len < sizeof(*pkt->udp)) return;
+ // MG_DEBUG((" UDP %u %u -> %u", len, mg_htons(udp->sport),
+ // mg_htons(udp->dport)));
+ mkpay(pkt, pkt->udp + 1);
+ }
+}
+
+static void mg_tcpip_rx(struct mg_tcpip_if *ifp, void *buf, size_t len) {
+ const uint8_t broadcast[] = {255, 255, 255, 255, 255, 255};
+ struct pkt pkt;
+ memset(&pkt, 0, sizeof(pkt));
+ pkt.raw.ptr = (char *) buf;
+ pkt.raw.len = len;
+ pkt.eth = (struct eth *) buf;
+ if (pkt.raw.len < sizeof(*pkt.eth)) return; // Truncated - runt?
+ if (ifp->enable_mac_check &&
+ memcmp(pkt.eth->dst, ifp->mac, sizeof(pkt.eth->dst)) != 0 &&
+ memcmp(pkt.eth->dst, broadcast, sizeof(pkt.eth->dst)) != 0)
+ return;
+ if (ifp->enable_crc32_check && len > 4) {
+ len -= 4; // TODO(scaprile): check on bigendian
+ uint32_t crc = mg_crc32(0, (const char *) buf, len);
+ if (memcmp((void *) ((size_t) buf + len), &crc, sizeof(crc))) return;
+ }
+ if (pkt.eth->type == mg_htons(0x806)) {
+ pkt.arp = (struct arp *) (pkt.eth + 1);
+ if (sizeof(*pkt.eth) + sizeof(*pkt.arp) > pkt.raw.len) return; // Truncated
+ rx_arp(ifp, &pkt);
+ } else if (pkt.eth->type == mg_htons(0x86dd)) {
+ pkt.ip6 = (struct ip6 *) (pkt.eth + 1);
+ if (pkt.raw.len < sizeof(*pkt.eth) + sizeof(*pkt.ip6)) return; // Truncated
+ if ((pkt.ip6->ver >> 4) != 0x6) return; // Not IP
+ mkpay(&pkt, pkt.ip6 + 1);
+ rx_ip6(ifp, &pkt);
+ } else if (pkt.eth->type == mg_htons(0x800)) {
+ pkt.ip = (struct ip *) (pkt.eth + 1);
+ if (pkt.raw.len < sizeof(*pkt.eth) + sizeof(*pkt.ip)) return; // Truncated
+ // Truncate frame to what IP header tells us
+ if ((size_t) mg_ntohs(pkt.ip->len) + sizeof(struct eth) < pkt.raw.len) {
+ pkt.raw.len = (size_t) mg_ntohs(pkt.ip->len) + sizeof(struct eth);
+ }
+ if (pkt.raw.len < sizeof(*pkt.eth) + sizeof(*pkt.ip)) return; // Truncated
+ if ((pkt.ip->ver >> 4) != 4) return; // Not IP
+ mkpay(&pkt, pkt.ip + 1);
+ rx_ip(ifp, &pkt);
+ } else {
+ MG_DEBUG((" Unknown eth type %x", mg_htons(pkt.eth->type)));
+ mg_hexdump(buf, len >= 16 ? 16 : len);
+ }
+}
+
+static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t uptime_ms) {
+ if (ifp == NULL || ifp->driver == NULL) return;
+ bool expired_1000ms = mg_timer_expired(&ifp->timer_1000ms, 1000, uptime_ms);
+ ifp->now = uptime_ms;
+
+ // Handle physical interface up/down status
+ if (expired_1000ms && ifp->driver->up) {
+ bool up = ifp->driver->up(ifp);
+ bool current = ifp->state != MG_TCPIP_STATE_DOWN;
+ if (up != current) {
+ ifp->state = up == false ? MG_TCPIP_STATE_DOWN
+ : ifp->enable_dhcp_client ? MG_TCPIP_STATE_UP
+ : MG_TCPIP_STATE_READY;
+ if (!up && ifp->enable_dhcp_client) ifp->ip = 0;
+ onstatechange(ifp);
+ }
+ }
+ if (ifp->state == MG_TCPIP_STATE_DOWN) return;
+
+ // If IP not configured, send DHCP
+ if (ifp->ip == 0 && expired_1000ms) tx_dhcp_discover(ifp);
+
+ // Read data from the network
+ if (ifp->driver->rx != NULL) { // Polling driver. We must call it
+ size_t len =
+ ifp->driver->rx(ifp->recv_queue.buf, ifp->recv_queue.size, ifp);
+ if (len > 0) mg_tcpip_rx(ifp, ifp->recv_queue.buf, len);
+ } else { // Interrupt-based driver. Fills recv queue itself
+ char *buf;
+ size_t len = mg_queue_next(&ifp->recv_queue, &buf);
+ if (len > 0) {
+ mg_tcpip_rx(ifp, buf, len);
+ mg_queue_del(&ifp->recv_queue, len);
+ }
+ }
+
+ // Process timeouts
+ for (struct mg_connection *c = ifp->mgr->conns; c != NULL; c = c->next) {
+ if (c->is_udp || c->is_listening) continue;
+ if (c->is_connecting || c->is_resolving) continue;
+ struct connstate *s = (struct connstate *) (c + 1);
+ if (uptime_ms > s->timer) {
+ if (s->ttype == MIP_TTYPE_ACK) {
+ MG_DEBUG(("%lu ack %x %x", c->id, s->seq, s->ack));
+ tx_tcp(ifp, s->mac, c->rem.ip, TH_ACK, c->loc.port, c->rem.port,
+ mg_htonl(s->seq), mg_htonl(s->ack), "", 0);
+ } else {
+ if (s->tmiss++ > 2) {
+ mg_error(c, "keepalive");
+ } else {
+ MG_DEBUG(("%lu keepalive", c->id));
+ tx_tcp(ifp, s->mac, c->rem.ip, TH_ACK, c->loc.port, c->rem.port,
+ mg_htonl(s->seq - 1), mg_htonl(s->ack), "", 0);
+ }
+ }
+ settmout(c, MIP_TTYPE_KEEPALIVE);
+ }
+ }
+}
+
+// This function executes in interrupt context, thus it should copy data
+// somewhere fast. Note that newlib's malloc is not thread safe, thus use
+// our lock-free queue with preallocated buffer to copy data and return asap
+void mg_tcpip_qwrite(void *buf, size_t len, struct mg_tcpip_if *ifp) {
+ char *p;
+ if (mg_queue_book(&ifp->recv_queue, &p, len) >= len) {
+ memcpy(p, buf, len);
+ mg_queue_add(&ifp->recv_queue, len);
+ ifp->nrecv++;
+ } else {
+ ifp->ndrop++;
+ }
+}
+
+void mg_tcpip_init(struct mg_mgr *mgr, struct mg_tcpip_if *ifp) {
+ // If MAC address is not set, make a random one
+ if (ifp->mac[0] == 0 && ifp->mac[1] == 0 && ifp->mac[2] == 0 &&
+ ifp->mac[3] == 0 && ifp->mac[4] == 0 && ifp->mac[5] == 0) {
+ ifp->mac[0] = 0x02; // Locally administered, unicast
+ mg_random(&ifp->mac[1], sizeof(ifp->mac) - 1);
+ MG_INFO(("MAC not set. Generated random: %M", mg_print_mac, ifp->mac));
+ }
+
+ if (ifp->driver->init && !ifp->driver->init(ifp)) {
+ MG_ERROR(("driver init failed"));
+ } else {
+ size_t framesize = 1540;
+ ifp->tx.ptr = (char *) calloc(1, framesize), ifp->tx.len = framesize;
+ if (ifp->recv_queue.size == 0)
+ ifp->recv_queue.size = ifp->driver->rx ? framesize : 8192;
+ ifp->recv_queue.buf = (char *) calloc(1, ifp->recv_queue.size);
+ ifp->timer_1000ms = mg_millis();
+ mgr->priv = ifp;
+ ifp->mgr = mgr;
+ mgr->extraconnsize = sizeof(struct connstate);
+ if (ifp->ip == 0) ifp->enable_dhcp_client = true;
+ memset(ifp->gwmac, 255, sizeof(ifp->gwmac)); // Set to broadcast
+ mg_random(&ifp->eport, sizeof(ifp->eport)); // Random from 0 to 65535
+ ifp->eport |=
+ MG_EPHEMERAL_PORT_BASE; // Random from MG_EPHEMERAL_PORT_BASE to 65535
+ if (ifp->tx.ptr == NULL || ifp->recv_queue.buf == NULL) MG_ERROR(("OOM"));
+ }
+}
+
+void mg_tcpip_free(struct mg_tcpip_if *ifp) {
+ free(ifp->recv_queue.buf);
+ free((char *) ifp->tx.ptr);
+}
+
+int mg_mkpipe(struct mg_mgr *m, mg_event_handler_t fn, void *d, bool udp) {
+ (void) m, (void) fn, (void) d, (void) udp;
+ MG_ERROR(("Not implemented"));
+ return -1;
+}
+
+static void send_syn(struct mg_connection *c) {
+ struct connstate *s = (struct connstate *) (c + 1);
+ uint32_t isn = mg_htonl((uint32_t) mg_ntohs(c->loc.port));
+ struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv;
+ tx_tcp(ifp, s->mac, c->rem.ip, TH_SYN, c->loc.port, c->rem.port, isn, 0, NULL,
+ 0);
+}
+
+void mg_connect_resolved(struct mg_connection *c) {
+ struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv;
+ c->is_resolving = 0;
+ if (ifp->eport < MG_EPHEMERAL_PORT_BASE) ifp->eport = MG_EPHEMERAL_PORT_BASE;
+ c->loc.ip = ifp->ip;
+ c->loc.port = mg_htons(ifp->eport++);
+ MG_DEBUG(("%lu %M -> %M", c->id, mg_print_ip_port, &c->loc, mg_print_ip_port,
+ &c->rem));
+ mg_call(c, MG_EV_RESOLVE, NULL);
+ if (((c->rem.ip & ifp->mask) == (ifp->ip & ifp->mask))) {
+ // If we're in the same LAN, fire an ARP lookup. TODO(cpq): handle this!
+ MG_DEBUG(("%lu ARP lookup...", c->id));
+ arp_ask(ifp, c->rem.ip);
+ c->is_arplooking = 1;
+ } else if (c->rem.ip == (ifp->ip | ~ifp->mask)) {
+ struct connstate *s = (struct connstate *) (c + 1);
+ memset(s->mac, 0xFF, sizeof(s->mac)); // local broadcast
+ } else if ((*((uint8_t *) &c->rem.ip) & 0xE0) == 0xE0) {
+ struct connstate *s = (struct connstate *) (c + 1); // 224 to 239, E0 to EF
+ uint8_t mcastp[3] = {0x01, 0x00, 0x5E}; // multicast group
+ memcpy(s->mac, mcastp, 3);
+ memcpy(s->mac + 3, ((uint8_t *) &c->rem.ip) + 1, 3); // 23 LSb
+ s->mac[3] &= 0x7F;
+ } else {
+ struct connstate *s = (struct connstate *) (c + 1);
+ memcpy(s->mac, ifp->gwmac, sizeof(ifp->gwmac));
+ if (c->is_udp) {
+ mg_call(c, MG_EV_CONNECT, NULL);
+ } else {
+ send_syn(c);
+ c->is_connecting = 1;
+ }
+ }
+}
+
+bool mg_open_listener(struct mg_connection *c, const char *url) {
+ c->loc.port = mg_htons(mg_url_port(url));
+ return true;
+}
+
+static void write_conn(struct mg_connection *c) {
+ long len = c->is_tls ? mg_tls_send(c, c->send.buf, c->send.len)
+ : mg_io_send(c, c->send.buf, c->send.len);
+ if (len > 0) {
+ mg_iobuf_del(&c->send, 0, (size_t) len);
+ mg_call(c, MG_EV_WRITE, &len);
+ }
+}
+
+static void close_conn(struct mg_connection *c) {
+ struct connstate *s = (struct connstate *) (c + 1);
+ mg_iobuf_free(&s->raw); // For TLS connections, release raw data
+ if (c->is_udp == false && c->is_listening == false) { // For TCP conns,
+ struct mg_tcpip_if *ifp =
+ (struct mg_tcpip_if *) c->mgr->priv; // send TCP FIN
+ tx_tcp(ifp, s->mac, c->rem.ip, TH_FIN | TH_ACK, c->loc.port, c->rem.port,
+ mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0);
+ }
+ mg_close_conn(c);
+}
+
+static bool can_write(struct mg_connection *c) {
+ return c->is_connecting == 0 && c->is_resolving == 0 && c->send.len > 0 &&
+ c->is_tls_hs == 0 && c->is_arplooking == 0;
+}
+
+void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
+ struct mg_connection *c, *tmp;
+ uint64_t now = mg_millis();
+ mg_tcpip_poll((struct mg_tcpip_if *) mgr->priv, now);
+ mg_timer_poll(&mgr->timers, now);
+ for (c = mgr->conns; c != NULL; c = tmp) {
+ tmp = c->next;
+ mg_call(c, MG_EV_POLL, &now);
+ MG_VERBOSE(("%lu .. %c%c%c%c%c", c->id, c->is_tls ? 'T' : 't',
+ c->is_connecting ? 'C' : 'c', c->is_tls_hs ? 'H' : 'h',
+ c->is_resolving ? 'R' : 'r', c->is_closing ? 'C' : 'c'));
+ if (c->is_tls_hs) mg_tls_handshake(c);
+ if (can_write(c)) write_conn(c);
+ if (c->is_draining && c->send.len == 0) c->is_closing = 1;
+ if (c->is_closing) close_conn(c);
+ }
+ (void) ms;
+}
+
+bool mg_send(struct mg_connection *c, const void *buf, size_t len) {
+ struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv;
+ bool res = false;
+ if (ifp->ip == 0 || ifp->state != MG_TCPIP_STATE_READY) {
+ mg_error(c, "net down");
+ } else if (c->is_udp) {
+ struct connstate *s = (struct connstate *) (c + 1);
+ tx_udp(ifp, s->mac, ifp->ip, c->loc.port, c->rem.ip, c->rem.port, buf, len);
+ res = true;
+ } else {
+ res = mg_iobuf_add(&c->send, c->send.len, buf, len);
+ }
+ return res;
+}
+#endif // MG_ENABLE_TCPIP
--- /dev/null
+// Copyright (c) 2004-2013 Sergey Lyubka
+// Copyright (c) 2013-2022 Cesanta Software Limited
+// All rights reserved
+//
+// This software is dual-licensed: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation. For the terms of this
+// license, see http://www.gnu.org/licenses/
+//
+// You are free to use this software under the terms of the GNU General
+// Public License, but WITHOUT ANY WARRANTY; without even the implied
+// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU General Public License for more details.
+//
+// Alternatively, you can license this software under a commercial
+// license, as set out in https://www.mongoose.ws/licensing/
+//
+// SPDX-License-Identifier: GPL-2.0-only or commercial
+
+#ifndef MONGOOSE_H
+#define MONGOOSE_H
+
+#define MG_VERSION "7.10"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define MG_ARCH_CUSTOM 0 // User creates its own mongoose_custom.h
+#define MG_ARCH_UNIX 1 // Linux, BSD, Mac, ...
+#define MG_ARCH_WIN32 2 // Windows
+#define MG_ARCH_ESP32 3 // ESP32
+#define MG_ARCH_ESP8266 4 // ESP8266
+#define MG_ARCH_FREERTOS 5 // FreeRTOS
+#define MG_ARCH_AZURERTOS 6 // MS Azure RTOS
+#define MG_ARCH_ZEPHYR 7 // Zephyr RTOS
+#define MG_ARCH_NEWLIB 8 // Bare metal ARM
+#define MG_ARCH_CMSIS_RTOS1 9 // CMSIS-RTOS API v1 (Keil RTX)
+#define MG_ARCH_TIRTOS 10 // Texas Semi TI-RTOS
+#define MG_ARCH_RP2040 11 // Raspberry Pi RP2040
+#define MG_ARCH_ARMCC 12 // Keil MDK-Core with Configuration Wizard
+#define MG_ARCH_CMSIS_RTOS2 13 // CMSIS-RTOS API v2 (Keil RTX5, FreeRTOS)
+
+#if !defined(MG_ARCH)
+#if defined(__unix__) || defined(__APPLE__)
+#define MG_ARCH MG_ARCH_UNIX
+#elif defined(_WIN32)
+#define MG_ARCH MG_ARCH_WIN32
+#elif defined(ICACHE_FLASH) || defined(ICACHE_RAM_ATTR)
+#define MG_ARCH MG_ARCH_ESP8266
+#elif defined(__ZEPHYR__)
+#define MG_ARCH MG_ARCH_ZEPHYR
+#elif defined(ESP_PLATFORM)
+#define MG_ARCH MG_ARCH_ESP32
+#elif defined(FREERTOS_IP_H)
+#define MG_ARCH MG_ARCH_FREERTOS
+#define MG_ENABLE_FREERTOS_TCP 1
+#elif defined(AZURE_RTOS_THREADX)
+#define MG_ARCH MG_ARCH_AZURERTOS
+#elif defined(PICO_TARGET_NAME)
+#define MG_ARCH MG_ARCH_RP2040
+#elif defined(__ARMCC_VERSION)
+#define MG_ARCH MG_ARCH_ARMCC
+#endif
+#endif // !defined(MG_ARCH)
+
+// if the user did not specify an MG_ARCH, or specified a custom one, OR
+// we guessed a known IDE, pull the customized config (Configuration Wizard)
+#if !defined(MG_ARCH) || (MG_ARCH == MG_ARCH_CUSTOM) || MG_ARCH == MG_ARCH_ARMCC
+#include "mongoose_custom.h" // keep this include
+#endif
+
+#if !defined(MG_ARCH)
+#error "MG_ARCH is not specified and we couldn't guess it. Set -D MG_ARCH=..."
+#endif
+
+// http://esr.ibiblio.org/?p=5095
+#define MG_BIG_ENDIAN (*(uint16_t *) "\0\xff" < 0x100)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#if MG_ARCH == MG_ARCH_AZURERTOS
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <time.h>
+
+#include <fx_api.h>
+#include <tx_api.h>
+
+#include <nx_api.h>
+#include <nx_bsd.h>
+#include <nx_port.h>
+#include <tx_port.h>
+
+#define PATH_MAX FX_MAXIMUM_PATH
+#define MG_DIRSEP '\\'
+
+#define socklen_t int
+#define closesocket(x) soc_close(x)
+
+#undef FOPEN_MAX
+
+#endif
+
+
+#if MG_ARCH == MG_ARCH_ESP32
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <netdb.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include <esp_timer.h>
+
+#define MG_PATH_MAX 128
+
+#endif
+
+
+#if MG_ARCH == MG_ARCH_ESP8266
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <netdb.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include <esp_system.h>
+
+#define MG_PATH_MAX 128
+
+#endif
+
+
+#if MG_ARCH == MG_ARCH_FREERTOS
+
+#include <ctype.h>
+#if !defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP
+#include <errno.h>
+#endif
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h> // rand(), strtol(), atoi()
+#include <string.h>
+#if defined(__ARMCC_VERSION)
+#define mode_t size_t
+#include <time.h>
+#else
+#include <sys/stat.h>
+#endif
+
+#include <FreeRTOS.h>
+#include <task.h>
+
+#ifndef MG_IO_SIZE
+#define MG_IO_SIZE 512
+#endif
+
+#define calloc(a, b) mg_calloc(a, b)
+#define free(a) vPortFree(a)
+#define malloc(a) pvPortMalloc(a)
+#define strdup(s) ((char *) mg_strdup(mg_str(s)).ptr)
+
+// Re-route calloc/free to the FreeRTOS's functions, don't use stdlib
+static inline void *mg_calloc(size_t cnt, size_t size) {
+ void *p = pvPortMalloc(cnt * size);
+ if (p != NULL) memset(p, 0, size * cnt);
+ return p;
+}
+
+#define mkdir(a, b) mg_mkdir(a, b)
+static inline int mg_mkdir(const char *path, mode_t mode) {
+ (void) path, (void) mode;
+ return -1;
+}
+
+#endif // MG_ARCH == MG_ARCH_FREERTOS
+
+
+#if MG_ARCH == MG_ARCH_NEWLIB
+#define _POSIX_TIMERS
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#define MG_PATH_MAX 100
+#define MG_ENABLE_SOCKET 0
+#define MG_ENABLE_DIRLIST 0
+
+#endif
+
+
+#if MG_ARCH == MG_ARCH_RP2040
+#include <errno.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <pico/stdlib.h>
+int mkdir(const char *, mode_t);
+#endif
+
+
+#if MG_ARCH == MG_ARCH_ARMCC || MG_ARCH == MG_ARCH_CMSIS_RTOS1 || \
+ MG_ARCH == MG_ARCH_CMSIS_RTOS2
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#if MG_ARCH == MG_ARCH_CMSIS_RTOS1
+#include "cmsis_os.h" // keep this include
+// https://developer.arm.com/documentation/ka003821/latest
+extern uint32_t rt_time_get(void);
+#elif MG_ARCH == MG_ARCH_CMSIS_RTOS2
+#include "cmsis_os2.h" // keep this include
+#endif
+
+#define strdup(s) ((char *) mg_strdup(mg_str(s)).ptr)
+
+#if defined(__ARMCC_VERSION)
+#define mode_t size_t
+#define mkdir(a, b) mg_mkdir(a, b)
+static inline int mg_mkdir(const char *path, mode_t mode) {
+ (void) path, (void) mode;
+ return -1;
+}
+#endif
+
+#if (MG_ARCH == MG_ARCH_CMSIS_RTOS1 || MG_ARCH == MG_ARCH_CMSIS_RTOS2) && \
+ !defined MG_ENABLE_RL && (!defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP) && \
+ (!defined(MG_ENABLE_TCPIP) || !MG_ENABLE_TCPIP)
+#define MG_ENABLE_RL 1
+#endif
+
+#endif
+
+
+#if MG_ARCH == MG_ARCH_TIRTOS
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#include <serrno.h>
+#include <sys/socket.h>
+
+#include <ti/sysbios/knl/Clock.h>
+
+#endif
+
+
+#if MG_ARCH == MG_ARCH_UNIX
+
+#define _DARWIN_UNLIMITED_SELECT 1 // No limit on file descriptors
+
+#if defined(__APPLE__)
+#include <mach/mach_time.h>
+#endif
+
+#if !defined(MG_ENABLE_EPOLL) && defined(__linux__)
+#define MG_ENABLE_EPOLL 1
+#elif !defined(MG_ENABLE_POLL)
+#define MG_ENABLE_POLL 1
+#endif
+
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if defined(MG_ENABLE_EPOLL) && MG_ENABLE_EPOLL
+#include <sys/epoll.h>
+#elif defined(MG_ENABLE_POLL) && MG_ENABLE_POLL
+#include <poll.h>
+#else
+#include <sys/select.h>
+#endif
+
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#ifndef MG_ENABLE_DIRLIST
+#define MG_ENABLE_DIRLIST 1
+#endif
+
+#ifndef MG_PATH_MAX
+#define MG_PATH_MAX FILENAME_MAX
+#endif
+
+#endif
+
+
+#if MG_ARCH == MG_ARCH_WIN32
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+
+#ifndef _CRT_SECURE_NO_WARNINGS
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS
+#define _WINSOCK_DEPRECATED_NO_WARNINGS
+#endif
+
+#include <ctype.h>
+#include <direct.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+
+#if defined(_MSC_VER) && _MSC_VER < 1700
+#define __func__ ""
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+typedef unsigned char uint8_t;
+typedef char int8_t;
+typedef unsigned short uint16_t;
+typedef short int16_t;
+typedef unsigned int uint32_t;
+typedef int int32_t;
+typedef enum { false = 0, true = 1 } bool;
+#else
+#include <stdbool.h>
+#include <stdint.h>
+#include <ws2tcpip.h>
+#endif
+
+#include <process.h>
+#include <winerror.h>
+#include <winsock2.h>
+
+// Protect from calls like std::snprintf in app code
+// See https://github.com/cesanta/mongoose/issues/1047
+#ifndef __cplusplus
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+#ifndef strdup // For MSVC with _DEBUG, see #1359
+#define strdup(x) _strdup(x)
+#endif
+#endif
+
+#define MG_INVALID_SOCKET INVALID_SOCKET
+#define MG_SOCKET_TYPE SOCKET
+typedef unsigned long nfds_t;
+#if defined(_MSC_VER)
+#pragma comment(lib, "ws2_32.lib")
+#ifndef alloca
+#define alloca(a) _alloca(a)
+#endif
+#endif
+#define poll(a, b, c) WSAPoll((a), (b), (c))
+#define closesocket(x) closesocket(x)
+
+typedef int socklen_t;
+#define MG_DIRSEP '\\'
+
+#ifndef MG_PATH_MAX
+#define MG_PATH_MAX FILENAME_MAX
+#endif
+
+#ifndef SO_EXCLUSIVEADDRUSE
+#define SO_EXCLUSIVEADDRUSE ((int) (~SO_REUSEADDR))
+#endif
+
+#define MG_SOCK_ERR(errcode) ((errcode) < 0 ? WSAGetLastError() : 0)
+
+#define MG_SOCK_PENDING(errcode) \
+ (((errcode) < 0) && \
+ (WSAGetLastError() == WSAEINTR || WSAGetLastError() == WSAEINPROGRESS || \
+ WSAGetLastError() == WSAEWOULDBLOCK))
+
+#define MG_SOCK_RESET(errcode) \
+ (((errcode) < 0) && (WSAGetLastError() == WSAECONNRESET))
+
+#define realpath(a, b) _fullpath((b), (a), MG_PATH_MAX)
+#define sleep(x) Sleep((x) *1000)
+#define mkdir(a, b) _mkdir(a)
+
+#ifndef S_ISDIR
+#define S_ISDIR(x) (((x) &_S_IFMT) == _S_IFDIR)
+#endif
+
+#ifndef MG_ENABLE_DIRLIST
+#define MG_ENABLE_DIRLIST 1
+#endif
+
+#ifndef SIGPIPE
+#define SIGPIPE 0
+#endif
+
+#endif
+
+
+#if MG_ARCH == MG_ARCH_ZEPHYR
+
+#include <zephyr/kernel.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <zephyr/net/socket.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h>
+
+#define MG_PUTCHAR(x) printk("%c", x)
+#ifndef strdup
+#define strdup(s) ((char *) mg_strdup(mg_str(s)).ptr)
+#endif
+#define strerror(x) zsock_gai_strerror(x)
+#define FD_CLOEXEC 0
+#define F_SETFD 0
+#define MG_ENABLE_SSI 0
+
+int rand(void);
+int sscanf(const char *, const char *, ...);
+
+#endif
+
+
+#if defined(MG_ENABLE_FREERTOS_TCP) && MG_ENABLE_FREERTOS_TCP
+
+#include <limits.h>
+#include <list.h>
+
+#include <FreeRTOS_IP.h>
+#include <FreeRTOS_Sockets.h>
+#include <FreeRTOS_errno_TCP.h> // contents to be moved and file removed, some day
+
+#define MG_SOCKET_TYPE Socket_t
+#define MG_INVALID_SOCKET FREERTOS_INVALID_SOCKET
+
+// Why FreeRTOS-TCP did not implement a clean BSD API, but its own thing
+// with FreeRTOS_ prefix, is beyond me
+#define IPPROTO_TCP FREERTOS_IPPROTO_TCP
+#define IPPROTO_UDP FREERTOS_IPPROTO_UDP
+#define AF_INET FREERTOS_AF_INET
+#define SOCK_STREAM FREERTOS_SOCK_STREAM
+#define SOCK_DGRAM FREERTOS_SOCK_DGRAM
+#define SO_BROADCAST 0
+#define SO_ERROR 0
+#define SOL_SOCKET 0
+#define SO_REUSEADDR 0
+
+#define MG_SOCK_ERR(errcode) ((errcode) < 0 ? (errcode) : 0)
+
+#define MG_SOCK_PENDING(errcode) \
+ ((errcode) == -pdFREERTOS_ERRNO_EWOULDBLOCK || \
+ (errcode) == -pdFREERTOS_ERRNO_EISCONN || \
+ (errcode) == -pdFREERTOS_ERRNO_EINPROGRESS || \
+ (errcode) == -pdFREERTOS_ERRNO_EAGAIN)
+
+#define MG_SOCK_RESET(errcode) ((errcode) == -pdFREERTOS_ERRNO_ENOTCONN)
+
+// actually only if optional timeout is enabled
+#define MG_SOCK_INTR(fd) (fd == NULL)
+
+#define sockaddr_in freertos_sockaddr
+#define sockaddr freertos_sockaddr
+#define accept(a, b, c) FreeRTOS_accept((a), (b), (c))
+#define connect(a, b, c) FreeRTOS_connect((a), (b), (c))
+#define bind(a, b, c) FreeRTOS_bind((a), (b), (c))
+#define listen(a, b) FreeRTOS_listen((a), (b))
+#define socket(a, b, c) FreeRTOS_socket((a), (b), (c))
+#define send(a, b, c, d) FreeRTOS_send((a), (b), (c), (d))
+#define recv(a, b, c, d) FreeRTOS_recv((a), (b), (c), (d))
+#define setsockopt(a, b, c, d, e) FreeRTOS_setsockopt((a), (b), (c), (d), (e))
+#define sendto(a, b, c, d, e, f) FreeRTOS_sendto((a), (b), (c), (d), (e), (f))
+#define recvfrom(a, b, c, d, e, f) \
+ FreeRTOS_recvfrom((a), (b), (c), (d), (e), (f))
+#define closesocket(x) FreeRTOS_closesocket(x)
+#define gethostbyname(x) FreeRTOS_gethostbyname(x)
+#define getsockname(a, b, c) mg_getsockname((a), (b), (c))
+#define getpeername(a, b, c) mg_getpeername((a), (b), (c))
+
+static inline int mg_getsockname(MG_SOCKET_TYPE fd, void *buf, socklen_t *len) {
+ (void) fd, (void) buf, (void) len;
+ return -1;
+}
+
+static inline int mg_getpeername(MG_SOCKET_TYPE fd, void *buf, socklen_t *len) {
+ (void) fd, (void) buf, (void) len;
+ return 0;
+}
+#endif
+
+
+#if defined(MG_ENABLE_LWIP) && MG_ENABLE_LWIP
+
+#if defined(__GNUC__) && !defined(__ARMCC_VERSION)
+#include <sys/stat.h>
+#endif
+
+struct timeval;
+
+#include <lwip/sockets.h>
+
+#if !LWIP_TIMEVAL_PRIVATE
+#if defined(__GNUC__) && !defined(__ARMCC_VERSION) // armclang sets both
+#include <sys/time.h>
+#else
+struct timeval {
+ time_t tv_sec;
+ long tv_usec;
+};
+#endif
+#endif
+
+#if LWIP_SOCKET != 1
+// Sockets support disabled in LWIP by default
+#error Set LWIP_SOCKET variable to 1 (in lwipopts.h)
+#endif
+#endif
+
+
+#if defined(MG_ENABLE_RL) && MG_ENABLE_RL
+#include <rl_net.h>
+
+#define closesocket(x) closesocket(x)
+
+#define TCP_NODELAY SO_KEEPALIVE
+
+#define MG_SOCK_ERR(errcode) ((errcode) < 0 ? (errcode) : 0)
+
+#define MG_SOCK_PENDING(errcode) \
+ ((errcode) == BSD_EWOULDBLOCK || (errcode) == BSD_EALREADY || \
+ (errcode) == BSD_EINPROGRESS)
+
+#define MG_SOCK_RESET(errcode) \
+ ((errcode) == BSD_ECONNABORTED || (errcode) == BSD_ECONNRESET)
+
+#define MG_SOCK_INTR(fd) 0
+
+#define socklen_t int
+#endif
+
+
+#ifndef MG_ENABLE_LOG
+#define MG_ENABLE_LOG 1
+#endif
+
+#ifndef MG_ENABLE_TCPIP
+#define MG_ENABLE_TCPIP 0 // Mongoose built-in network stack
+#endif
+
+#ifndef MG_ENABLE_LWIP
+#define MG_ENABLE_LWIP 0 // lWIP network stack
+#endif
+
+#ifndef MG_ENABLE_FREERTOS_TCP
+#define MG_ENABLE_FREERTOS_TCP 0 // Amazon FreeRTOS-TCP network stack
+#endif
+
+#ifndef MG_ENABLE_RL
+#define MG_ENABLE_RL 0 // ARM MDK network stack
+#endif
+
+#ifndef MG_ENABLE_SOCKET
+#define MG_ENABLE_SOCKET !MG_ENABLE_TCPIP
+#endif
+
+#ifndef MG_ENABLE_POLL
+#define MG_ENABLE_POLL 0
+#endif
+
+#ifndef MG_ENABLE_EPOLL
+#define MG_ENABLE_EPOLL 0
+#endif
+
+#ifndef MG_ENABLE_FATFS
+#define MG_ENABLE_FATFS 0
+#endif
+
+#ifndef MG_ENABLE_MBEDTLS
+#define MG_ENABLE_MBEDTLS 0
+#endif
+
+#ifndef MG_ENABLE_OPENSSL
+#define MG_ENABLE_OPENSSL 0
+#endif
+
+#ifndef MG_ENABLE_CUSTOM_TLS
+#define MG_ENABLE_CUSTOM_TLS 0
+#endif
+
+#ifndef MG_ENABLE_SSI
+#define MG_ENABLE_SSI 0
+#endif
+
+#ifndef MG_ENABLE_IPV6
+#define MG_ENABLE_IPV6 0
+#endif
+
+#ifndef MG_ENABLE_MD5
+#define MG_ENABLE_MD5 1
+#endif
+
+// Set MG_ENABLE_WINSOCK=0 for Win32 builds with external IP stack (like LWIP)
+#ifndef MG_ENABLE_WINSOCK
+#define MG_ENABLE_WINSOCK 1
+#endif
+
+#ifndef MG_ENABLE_DIRLIST
+#define MG_ENABLE_DIRLIST 0
+#endif
+
+#ifndef MG_ENABLE_CUSTOM_RANDOM
+#define MG_ENABLE_CUSTOM_RANDOM 0
+#endif
+
+#ifndef MG_ENABLE_CUSTOM_MILLIS
+#define MG_ENABLE_CUSTOM_MILLIS 0
+#endif
+
+#ifndef MG_ENABLE_PACKED_FS
+#define MG_ENABLE_PACKED_FS 0
+#endif
+
+#ifndef MG_ENABLE_ASSERT
+#define MG_ENABLE_ASSERT 0
+#endif
+
+#ifndef MG_IO_SIZE
+#define MG_IO_SIZE 2048 // Granularity of the send/recv IO buffer growth
+#endif
+
+#ifndef MG_MAX_RECV_SIZE
+#define MG_MAX_RECV_SIZE (3 * 1024 * 1024) // Maximum recv IO buffer size
+#endif
+
+#ifndef MG_DATA_SIZE
+#define MG_DATA_SIZE 32 // struct mg_connection :: data size
+#endif
+
+#ifndef MG_MAX_HTTP_HEADERS
+#define MG_MAX_HTTP_HEADERS 30
+#endif
+
+#ifndef MG_HTTP_INDEX
+#define MG_HTTP_INDEX "index.html"
+#endif
+
+#ifndef MG_PATH_MAX
+#ifdef PATH_MAX
+#define MG_PATH_MAX PATH_MAX
+#else
+#define MG_PATH_MAX 128
+#endif
+#endif
+
+#ifndef MG_SOCK_LISTEN_BACKLOG_SIZE
+#define MG_SOCK_LISTEN_BACKLOG_SIZE 3
+#endif
+
+#ifndef MG_DIRSEP
+#define MG_DIRSEP '/'
+#endif
+
+#ifndef MG_ENABLE_FILE
+#if defined(FOPEN_MAX)
+#define MG_ENABLE_FILE 1
+#else
+#define MG_ENABLE_FILE 0
+#endif
+#endif
+
+#ifndef MG_INVALID_SOCKET
+#define MG_INVALID_SOCKET (-1)
+#endif
+
+#ifndef MG_SOCKET_TYPE
+#define MG_SOCKET_TYPE int
+#endif
+
+#ifndef MG_SOCKET_ERRNO
+#define MG_SOCKET_ERRNO errno
+#endif
+
+#if MG_ENABLE_EPOLL
+#define MG_EPOLL_ADD(c) \
+ do { \
+ struct epoll_event ev = {EPOLLIN | EPOLLERR | EPOLLHUP, {c}}; \
+ epoll_ctl(c->mgr->epoll_fd, EPOLL_CTL_ADD, (int) (size_t) c->fd, &ev); \
+ } while (0)
+#define MG_EPOLL_MOD(c, wr) \
+ do { \
+ struct epoll_event ev = {EPOLLIN | EPOLLERR | EPOLLHUP, {c}}; \
+ if (wr) ev.events |= EPOLLOUT; \
+ epoll_ctl(c->mgr->epoll_fd, EPOLL_CTL_MOD, (int) (size_t) c->fd, &ev); \
+ } while (0)
+#else
+#define MG_EPOLL_ADD(c)
+#define MG_EPOLL_MOD(c, wr)
+#endif
+
+
+
+
+struct mg_str {
+ const char *ptr; // Pointer to string data
+ size_t len; // String len
+};
+
+#define MG_NULL_STR \
+ { NULL, 0 }
+
+#define MG_C_STR(a) \
+ { (a), sizeof(a) - 1 }
+
+// Using macro to avoid shadowing C++ struct constructor, see #1298
+#define mg_str(s) mg_str_s(s)
+
+struct mg_str mg_str(const char *s);
+struct mg_str mg_str_n(const char *s, size_t n);
+int mg_lower(const char *s);
+int mg_ncasecmp(const char *s1, const char *s2, size_t len);
+int mg_casecmp(const char *s1, const char *s2);
+int mg_vcmp(const struct mg_str *s1, const char *s2);
+int mg_vcasecmp(const struct mg_str *str1, const char *str2);
+int mg_strcmp(const struct mg_str str1, const struct mg_str str2);
+struct mg_str mg_strstrip(struct mg_str s);
+struct mg_str mg_strdup(const struct mg_str s);
+const char *mg_strstr(const struct mg_str haystack, const struct mg_str needle);
+bool mg_match(struct mg_str str, struct mg_str pattern, struct mg_str *caps);
+bool mg_globmatch(const char *pattern, size_t plen, const char *s, size_t n);
+bool mg_commalist(struct mg_str *s, struct mg_str *k, struct mg_str *v);
+bool mg_split(struct mg_str *s, struct mg_str *k, struct mg_str *v, char delim);
+char *mg_hex(const void *buf, size_t len, char *dst);
+void mg_unhex(const char *buf, size_t len, unsigned char *to);
+unsigned long mg_unhexn(const char *s, size_t len);
+int mg_check_ip_acl(struct mg_str acl, uint32_t remote_ip);
+char *mg_remove_double_dots(char *s);
+
+
+
+
+// Single producer, single consumer non-blocking queue
+
+struct mg_queue {
+ char *buf;
+ size_t size;
+ volatile size_t tail;
+ volatile size_t head;
+};
+
+void mg_queue_init(struct mg_queue *, char *, size_t); // Init queue
+size_t mg_queue_book(struct mg_queue *, char **buf, size_t); // Reserve space
+void mg_queue_add(struct mg_queue *, size_t); // Add new message
+size_t mg_queue_next(struct mg_queue *, char **); // Get oldest message
+void mg_queue_del(struct mg_queue *, size_t); // Delete oldest message
+
+
+
+
+typedef void (*mg_pfn_t)(char, void *); // Output function
+typedef size_t (*mg_pm_t)(mg_pfn_t, void *, va_list *); // %M printer
+
+size_t mg_vxprintf(void (*)(char, void *), void *, const char *fmt, va_list *);
+size_t mg_xprintf(void (*fn)(char, void *), void *, const char *fmt, ...);
+
+
+
+
+
+
+// Convenience wrappers around mg_xprintf
+size_t mg_vsnprintf(char *buf, size_t len, const char *fmt, va_list *ap);
+size_t mg_snprintf(char *, size_t, const char *fmt, ...);
+char *mg_vmprintf(const char *fmt, va_list *ap);
+char *mg_mprintf(const char *fmt, ...);
+size_t mg_queue_vprintf(struct mg_queue *, const char *fmt, va_list *);
+size_t mg_queue_printf(struct mg_queue *, const char *fmt, ...);
+
+// %M print helper functions
+size_t mg_print_base64(void (*out)(char, void *), void *arg, va_list *ap);
+size_t mg_print_esc(void (*out)(char, void *), void *arg, va_list *ap);
+size_t mg_print_hex(void (*out)(char, void *), void *arg, va_list *ap);
+size_t mg_print_ip(void (*out)(char, void *), void *arg, va_list *ap);
+size_t mg_print_ip_port(void (*out)(char, void *), void *arg, va_list *ap);
+size_t mg_print_ip4(void (*out)(char, void *), void *arg, va_list *ap);
+size_t mg_print_ip6(void (*out)(char, void *), void *arg, va_list *ap);
+size_t mg_print_mac(void (*out)(char, void *), void *arg, va_list *ap);
+
+// Various output functions
+void mg_pfn_iobuf(char ch, void *param); // param: struct mg_iobuf *
+void mg_pfn_stdout(char c, void *param); // param: ignored
+
+// A helper macro for printing JSON: mg_snprintf(buf, len, "%m", MG_ESC("hi"))
+#define MG_ESC(str) mg_print_esc, 0, (str)
+
+
+
+
+
+
+enum { MG_LL_NONE, MG_LL_ERROR, MG_LL_INFO, MG_LL_DEBUG, MG_LL_VERBOSE };
+void mg_log(const char *fmt, ...);
+bool mg_log_prefix(int ll, const char *file, int line, const char *fname);
+void mg_log_set(int log_level);
+void mg_hexdump(const void *buf, size_t len);
+void mg_log_set_fn(mg_pfn_t fn, void *param);
+
+#if MG_ENABLE_LOG
+#define MG_LOG(level, args) \
+ do { \
+ if (mg_log_prefix((level), __FILE__, __LINE__, __func__)) mg_log args; \
+ } while (0)
+#else
+#define MG_LOG(level, args) \
+ do { \
+ if (0) mg_log args; \
+ } while (0)
+#endif
+
+#define MG_ERROR(args) MG_LOG(MG_LL_ERROR, args)
+#define MG_INFO(args) MG_LOG(MG_LL_INFO, args)
+#define MG_DEBUG(args) MG_LOG(MG_LL_DEBUG, args)
+#define MG_VERBOSE(args) MG_LOG(MG_LL_VERBOSE, args)
+
+
+
+
+struct mg_timer {
+ unsigned long id; // Timer ID
+ uint64_t period_ms; // Timer period in milliseconds
+ uint64_t expire; // Expiration timestamp in milliseconds
+ unsigned flags; // Possible flags values below
+#define MG_TIMER_ONCE 0 // Call function once
+#define MG_TIMER_REPEAT 1 // Call function periodically
+#define MG_TIMER_RUN_NOW 2 // Call immediately when timer is set
+ void (*fn)(void *); // Function to call
+ void *arg; // Function argument
+ struct mg_timer *next; // Linkage
+};
+
+void mg_timer_init(struct mg_timer **head, struct mg_timer *timer,
+ uint64_t milliseconds, unsigned flags, void (*fn)(void *),
+ void *arg);
+void mg_timer_free(struct mg_timer **head, struct mg_timer *);
+void mg_timer_poll(struct mg_timer **head, uint64_t new_ms);
+bool mg_timer_expired(uint64_t *expiration, uint64_t period, uint64_t now);
+
+
+
+
+
+enum { MG_FS_READ = 1, MG_FS_WRITE = 2, MG_FS_DIR = 4 };
+
+// Filesystem API functions
+// st() returns MG_FS_* flags and populates file size and modification time
+// ls() calls fn() for every directory entry, allowing to list a directory
+//
+// NOTE: UNIX-style shorthand names for the API functions are deliberately
+// chosen to avoid conflicts with some libraries that make macros for e.g.
+// stat(), write(), read() calls.
+struct mg_fs {
+ int (*st)(const char *path, size_t *size, time_t *mtime); // stat file
+ void (*ls)(const char *path, void (*fn)(const char *, void *), void *);
+ void *(*op)(const char *path, int flags); // Open file
+ void (*cl)(void *fd); // Close file
+ size_t (*rd)(void *fd, void *buf, size_t len); // Read file
+ size_t (*wr)(void *fd, const void *buf, size_t len); // Write file
+ size_t (*sk)(void *fd, size_t offset); // Set file position
+ bool (*mv)(const char *from, const char *to); // Rename file
+ bool (*rm)(const char *path); // Delete file
+ bool (*mkd)(const char *path); // Create directory
+};
+
+extern struct mg_fs mg_fs_posix; // POSIX open/close/read/write/seek
+extern struct mg_fs mg_fs_packed; // Packed FS, see examples/device-dashboard
+extern struct mg_fs mg_fs_fat; // FAT FS
+
+// File descriptor
+struct mg_fd {
+ void *fd;
+ struct mg_fs *fs;
+};
+
+struct mg_fd *mg_fs_open(struct mg_fs *fs, const char *path, int flags);
+void mg_fs_close(struct mg_fd *fd);
+char *mg_file_read(struct mg_fs *fs, const char *path, size_t *size);
+bool mg_file_write(struct mg_fs *fs, const char *path, const void *, size_t);
+bool mg_file_printf(struct mg_fs *fs, const char *path, const char *fmt, ...);
+
+
+
+
+
+
+
+#if MG_ENABLE_ASSERT
+#include <assert.h>
+#elif !defined(assert)
+#define assert(x)
+#endif
+
+void mg_random(void *buf, size_t len);
+char *mg_random_str(char *buf, size_t len);
+uint16_t mg_ntohs(uint16_t net);
+uint32_t mg_ntohl(uint32_t net);
+uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len);
+uint64_t mg_millis(void);
+
+#define mg_htons(x) mg_ntohs(x)
+#define mg_htonl(x) mg_ntohl(x)
+
+#define MG_U32(a, b, c, d) \
+ (((uint32_t) ((a) &255) << 24) | ((uint32_t) ((b) &255) << 16) | \
+ ((uint32_t) ((c) &255) << 8) | (uint32_t) ((d) &255))
+
+// For printing IPv4 addresses: printf("%d.%d.%d.%d\n", MG_IPADDR_PARTS(&ip))
+#define MG_U8P(ADDR) ((uint8_t *) (ADDR))
+#define MG_IPADDR_PARTS(ADDR) \
+ MG_U8P(ADDR)[0], MG_U8P(ADDR)[1], MG_U8P(ADDR)[2], MG_U8P(ADDR)[3]
+
+// Linked list management macros
+#define LIST_ADD_HEAD(type_, head_, elem_) \
+ do { \
+ (elem_)->next = (*head_); \
+ *(head_) = (elem_); \
+ } while (0)
+
+#define LIST_ADD_TAIL(type_, head_, elem_) \
+ do { \
+ type_ **h = head_; \
+ while (*h != NULL) h = &(*h)->next; \
+ *h = (elem_); \
+ } while (0)
+
+#define LIST_DELETE(type_, head_, elem_) \
+ do { \
+ type_ **h = head_; \
+ while (*h != (elem_)) h = &(*h)->next; \
+ *h = (elem_)->next; \
+ } while (0)
+
+
+
+unsigned short mg_url_port(const char *url);
+int mg_url_is_ssl(const char *url);
+struct mg_str mg_url_host(const char *url);
+struct mg_str mg_url_user(const char *url);
+struct mg_str mg_url_pass(const char *url);
+const char *mg_url_uri(const char *url);
+
+
+
+
+struct mg_iobuf {
+ unsigned char *buf; // Pointer to stored data
+ size_t size; // Total size available
+ size_t len; // Current number of bytes
+ size_t align; // Alignment during allocation
+};
+
+int mg_iobuf_init(struct mg_iobuf *, size_t, size_t);
+int mg_iobuf_resize(struct mg_iobuf *, size_t);
+void mg_iobuf_free(struct mg_iobuf *);
+size_t mg_iobuf_add(struct mg_iobuf *, size_t, const void *, size_t);
+size_t mg_iobuf_del(struct mg_iobuf *, size_t ofs, size_t len);
+
+int mg_base64_update(unsigned char p, char *to, int len);
+int mg_base64_final(char *to, int len);
+int mg_base64_encode(const unsigned char *p, int n, char *to);
+int mg_base64_decode(const char *src, int n, char *dst);
+
+
+
+
+typedef struct {
+ uint32_t buf[4];
+ uint32_t bits[2];
+ unsigned char in[64];
+} mg_md5_ctx;
+
+void mg_md5_init(mg_md5_ctx *c);
+void mg_md5_update(mg_md5_ctx *c, const unsigned char *data, size_t len);
+void mg_md5_final(mg_md5_ctx *c, unsigned char[16]);
+
+
+
+
+typedef struct {
+ uint32_t state[5];
+ uint32_t count[2];
+ unsigned char buffer[64];
+} mg_sha1_ctx;
+
+void mg_sha1_init(mg_sha1_ctx *);
+void mg_sha1_update(mg_sha1_ctx *, const unsigned char *data, size_t len);
+void mg_sha1_final(unsigned char digest[20], mg_sha1_ctx *);
+
+
+struct mg_connection;
+typedef void (*mg_event_handler_t)(struct mg_connection *, int ev,
+ void *ev_data, void *fn_data);
+void mg_call(struct mg_connection *c, int ev, void *ev_data);
+void mg_error(struct mg_connection *c, const char *fmt, ...);
+
+enum {
+ MG_EV_ERROR, // Error char *error_message
+ MG_EV_OPEN, // Connection created NULL
+ MG_EV_POLL, // mg_mgr_poll iteration uint64_t *uptime_millis
+ MG_EV_RESOLVE, // Host name is resolved NULL
+ MG_EV_CONNECT, // Connection established NULL
+ MG_EV_ACCEPT, // Connection accepted NULL
+ MG_EV_TLS_HS, // TLS handshake succeeded NULL
+ MG_EV_READ, // Data received from socket long *bytes_read
+ MG_EV_WRITE, // Data written to socket long *bytes_written
+ MG_EV_CLOSE, // Connection closed NULL
+ MG_EV_HTTP_MSG, // HTTP request/response struct mg_http_message *
+ MG_EV_HTTP_CHUNK, // HTTP chunk (partial msg) struct mg_http_message *
+ MG_EV_WS_OPEN, // Websocket handshake done struct mg_http_message *
+ MG_EV_WS_MSG, // Websocket msg, text or bin struct mg_ws_message *
+ MG_EV_WS_CTL, // Websocket control msg struct mg_ws_message *
+ MG_EV_MQTT_CMD, // MQTT low-level command struct mg_mqtt_message *
+ MG_EV_MQTT_MSG, // MQTT PUBLISH received struct mg_mqtt_message *
+ MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code
+ MG_EV_SNTP_TIME, // SNTP time received uint64_t *epoch_millis
+ MG_EV_USER // Starting ID for user events
+};
+
+
+
+
+
+
+
+
+
+struct mg_dns {
+ const char *url; // DNS server URL
+ struct mg_connection *c; // DNS server connection
+};
+
+struct mg_addr {
+ uint16_t port; // TCP or UDP port in network byte order
+ uint32_t ip; // IP address in network byte order
+ uint8_t ip6[16]; // IPv6 address
+ bool is_ip6; // True when address is IPv6 address
+};
+
+struct mg_mgr {
+ struct mg_connection *conns; // List of active connections
+ struct mg_dns dns4; // DNS for IPv4
+ struct mg_dns dns6; // DNS for IPv6
+ int dnstimeout; // DNS resolve timeout in milliseconds
+ bool use_dns6; // Use DNS6 server by default, see #1532
+ unsigned long nextid; // Next connection ID
+ unsigned long timerid; // Next timer ID
+ void *userdata; // Arbitrary user data pointer
+ uint16_t mqtt_id; // MQTT IDs for pub/sub
+ void *active_dns_requests; // DNS requests in progress
+ struct mg_timer *timers; // Active timers
+ int epoll_fd; // Used when MG_EPOLL_ENABLE=1
+ void *priv; // Used by the MIP stack
+ size_t extraconnsize; // Used by the MIP stack
+#if MG_ENABLE_FREERTOS_TCP
+ SocketSet_t ss; // NOTE(lsm): referenced from socket struct
+#endif
+};
+
+struct mg_connection {
+ struct mg_connection *next; // Linkage in struct mg_mgr :: connections
+ struct mg_mgr *mgr; // Our container
+ struct mg_addr loc; // Local address
+ struct mg_addr rem; // Remote address
+ void *fd; // Connected socket, or LWIP data
+ unsigned long id; // Auto-incrementing unique connection ID
+ struct mg_iobuf recv; // Incoming data
+ struct mg_iobuf send; // Outgoing data
+ mg_event_handler_t fn; // User-specified event handler function
+ void *fn_data; // User-specified function parameter
+ mg_event_handler_t pfn; // Protocol-specific handler function
+ void *pfn_data; // Protocol-specific function parameter
+ char data[MG_DATA_SIZE]; // Arbitrary connection data
+ void *tls; // TLS specific data
+ unsigned is_listening : 1; // Listening connection
+ unsigned is_client : 1; // Outbound (client) connection
+ unsigned is_accepted : 1; // Accepted (server) connection
+ unsigned is_resolving : 1; // Non-blocking DNS resolution is in progress
+ unsigned is_arplooking : 1; // Non-blocking ARP resolution is in progress
+ unsigned is_connecting : 1; // Non-blocking connect is in progress
+ unsigned is_tls : 1; // TLS-enabled connection
+ unsigned is_tls_hs : 1; // TLS handshake is in progress
+ unsigned is_udp : 1; // UDP connection
+ unsigned is_websocket : 1; // WebSocket connection
+ unsigned is_mqtt5 : 1; // For MQTT connection, v5 indicator
+ unsigned is_hexdumping : 1; // Hexdump in/out traffic
+ unsigned is_draining : 1; // Send remaining data, then close and free
+ unsigned is_closing : 1; // Close and free the connection immediately
+ unsigned is_full : 1; // Stop reads, until cleared
+ unsigned is_resp : 1; // Response is still being generated
+ unsigned is_readable : 1; // Connection is ready to read
+ unsigned is_writable : 1; // Connection is ready to write
+};
+
+void mg_mgr_poll(struct mg_mgr *, int ms);
+void mg_mgr_init(struct mg_mgr *);
+void mg_mgr_free(struct mg_mgr *);
+
+struct mg_connection *mg_listen(struct mg_mgr *, const char *url,
+ mg_event_handler_t fn, void *fn_data);
+struct mg_connection *mg_connect(struct mg_mgr *, const char *url,
+ mg_event_handler_t fn, void *fn_data);
+struct mg_connection *mg_wrapfd(struct mg_mgr *mgr, int fd,
+ mg_event_handler_t fn, void *fn_data);
+void mg_connect_resolved(struct mg_connection *);
+bool mg_send(struct mg_connection *, const void *, size_t);
+size_t mg_printf(struct mg_connection *, const char *fmt, ...);
+size_t mg_vprintf(struct mg_connection *, const char *fmt, va_list *ap);
+bool mg_aton(struct mg_str str, struct mg_addr *addr);
+int mg_mkpipe(struct mg_mgr *, mg_event_handler_t, void *, bool udp);
+
+// These functions are used to integrate with custom network stacks
+struct mg_connection *mg_alloc_conn(struct mg_mgr *);
+void mg_close_conn(struct mg_connection *c);
+bool mg_open_listener(struct mg_connection *c, const char *url);
+
+// Utility functions
+struct mg_timer *mg_timer_add(struct mg_mgr *mgr, uint64_t milliseconds,
+ unsigned flags, void (*fn)(void *), void *arg);
+
+// Low-level IO primives used by TLS layer
+enum { MG_IO_ERR = -1, MG_IO_WAIT = -2, MG_IO_RESET = -3 };
+long mg_io_send(struct mg_connection *c, const void *buf, size_t len);
+long mg_io_recv(struct mg_connection *c, void *buf, size_t len);
+
+
+
+
+
+
+
+
+struct mg_http_header {
+ struct mg_str name; // Header name
+ struct mg_str value; // Header value
+};
+
+struct mg_http_message {
+ struct mg_str method, uri, query, proto; // Request/response line
+ struct mg_http_header headers[MG_MAX_HTTP_HEADERS]; // Headers
+ struct mg_str body; // Body
+ struct mg_str head; // Request + headers
+ struct mg_str chunk; // Chunk for chunked encoding, or partial body
+ struct mg_str message; // Request + headers + body
+};
+
+// Parameter for mg_http_serve_dir()
+struct mg_http_serve_opts {
+ const char *root_dir; // Web root directory, must be non-NULL
+ const char *ssi_pattern; // SSI file name pattern, e.g. #.shtml
+ const char *extra_headers; // Extra HTTP headers to add in responses
+ const char *mime_types; // Extra mime types, ext1=type1,ext2=type2,..
+ const char *page404; // Path to the 404 page, or NULL by default
+ struct mg_fs *fs; // Filesystem implementation. Use NULL for POSIX
+};
+
+// Parameter for mg_http_next_multipart
+struct mg_http_part {
+ struct mg_str name; // Form field name
+ struct mg_str filename; // Filename for file uploads
+ struct mg_str body; // Part contents
+};
+
+int mg_http_parse(const char *s, size_t len, struct mg_http_message *);
+int mg_http_get_request_len(const unsigned char *buf, size_t buf_len);
+void mg_http_printf_chunk(struct mg_connection *cnn, const char *fmt, ...);
+void mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len);
+void mg_http_delete_chunk(struct mg_connection *c, struct mg_http_message *hm);
+struct mg_connection *mg_http_listen(struct mg_mgr *, const char *url,
+ mg_event_handler_t fn, void *fn_data);
+struct mg_connection *mg_http_connect(struct mg_mgr *, const char *url,
+ mg_event_handler_t fn, void *fn_data);
+void mg_http_serve_dir(struct mg_connection *, struct mg_http_message *hm,
+ const struct mg_http_serve_opts *);
+void mg_http_serve_file(struct mg_connection *, struct mg_http_message *hm,
+ const char *path, const struct mg_http_serve_opts *);
+void mg_http_reply(struct mg_connection *, int status_code, const char *headers,
+ const char *body_fmt, ...);
+struct mg_str *mg_http_get_header(struct mg_http_message *, const char *name);
+struct mg_str mg_http_var(struct mg_str buf, struct mg_str name);
+int mg_http_get_var(const struct mg_str *, const char *name, char *, size_t);
+int mg_url_decode(const char *s, size_t n, char *to, size_t to_len, int form);
+size_t mg_url_encode(const char *s, size_t n, char *buf, size_t len);
+void mg_http_creds(struct mg_http_message *, char *, size_t, char *, size_t);
+bool mg_http_match_uri(const struct mg_http_message *, const char *glob);
+long mg_http_upload(struct mg_connection *c, struct mg_http_message *hm,
+ struct mg_fs *fs, const char *path, size_t max_size);
+void mg_http_bauth(struct mg_connection *, const char *user, const char *pass);
+struct mg_str mg_http_get_header_var(struct mg_str s, struct mg_str v);
+size_t mg_http_next_multipart(struct mg_str, size_t, struct mg_http_part *);
+int mg_http_status(const struct mg_http_message *hm);
+void mg_hello(const char *url);
+
+
+void mg_http_serve_ssi(struct mg_connection *c, const char *root,
+ const char *fullpath);
+
+
+
+
+
+
+struct mg_tls_opts {
+ const char *ca; // CA certificate file. For both listeners and clients
+ const char *crl; // Certificate Revocation List. For clients
+ const char *cert; // Certificate
+ const char *certkey; // Certificate key
+ const char *ciphers; // Cipher list
+ struct mg_str srvname; // If not empty, enables server name verification
+ struct mg_fs *fs; // FS API for reading certificate files
+};
+
+void mg_tls_init(struct mg_connection *, const struct mg_tls_opts *);
+void mg_tls_free(struct mg_connection *);
+long mg_tls_send(struct mg_connection *, const void *buf, size_t len);
+long mg_tls_recv(struct mg_connection *, void *buf, size_t len);
+size_t mg_tls_pending(struct mg_connection *);
+void mg_tls_handshake(struct mg_connection *);
+
+
+
+
+
+
+
+#if MG_ENABLE_MBEDTLS
+#include <mbedtls/debug.h>
+#include <mbedtls/net_sockets.h>
+#include <mbedtls/ssl.h>
+
+struct mg_tls {
+ char *cafile; // CA certificate path
+ mbedtls_x509_crt ca; // Parsed CA certificate
+ mbedtls_x509_crt cert; // Parsed certificate
+ mbedtls_ssl_context ssl; // SSL/TLS context
+ mbedtls_ssl_config conf; // SSL-TLS config
+ mbedtls_pk_context pk; // Private key context
+};
+#endif
+
+
+#if MG_ENABLE_OPENSSL
+
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+
+struct mg_tls {
+ SSL_CTX *ctx;
+ SSL *ssl;
+};
+#endif
+
+
+#define WEBSOCKET_OP_CONTINUE 0
+#define WEBSOCKET_OP_TEXT 1
+#define WEBSOCKET_OP_BINARY 2
+#define WEBSOCKET_OP_CLOSE 8
+#define WEBSOCKET_OP_PING 9
+#define WEBSOCKET_OP_PONG 10
+
+
+
+struct mg_ws_message {
+ struct mg_str data; // Websocket message data
+ uint8_t flags; // Websocket message flags
+};
+
+struct mg_connection *mg_ws_connect(struct mg_mgr *, const char *url,
+ mg_event_handler_t fn, void *fn_data,
+ const char *fmt, ...);
+void mg_ws_upgrade(struct mg_connection *, struct mg_http_message *,
+ const char *fmt, ...);
+size_t mg_ws_send(struct mg_connection *, const void *buf, size_t len, int op);
+size_t mg_ws_wrap(struct mg_connection *, size_t len, int op);
+size_t mg_ws_printf(struct mg_connection *c, int op, const char *fmt, ...);
+size_t mg_ws_vprintf(struct mg_connection *c, int op, const char *fmt,
+ va_list *);
+
+
+
+
+struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr, const char *url,
+ mg_event_handler_t fn, void *fn_data);
+void mg_sntp_request(struct mg_connection *c);
+int64_t mg_sntp_parse(const unsigned char *buf, size_t len);
+
+
+
+
+
+#define MQTT_CMD_CONNECT 1
+#define MQTT_CMD_CONNACK 2
+#define MQTT_CMD_PUBLISH 3
+#define MQTT_CMD_PUBACK 4
+#define MQTT_CMD_PUBREC 5
+#define MQTT_CMD_PUBREL 6
+#define MQTT_CMD_PUBCOMP 7
+#define MQTT_CMD_SUBSCRIBE 8
+#define MQTT_CMD_SUBACK 9
+#define MQTT_CMD_UNSUBSCRIBE 10
+#define MQTT_CMD_UNSUBACK 11
+#define MQTT_CMD_PINGREQ 12
+#define MQTT_CMD_PINGRESP 13
+#define MQTT_CMD_DISCONNECT 14
+#define MQTT_CMD_AUTH 15
+
+#define MQTT_PROP_PAYLOAD_FORMAT_INDICATOR 0x01
+#define MQTT_PROP_MESSAGE_EXPIRY_INTERVAL 0x02
+#define MQTT_PROP_CONTENT_TYPE 0x03
+#define MQTT_PROP_RESPONSE_TOPIC 0x08
+#define MQTT_PROP_CORRELATION_DATA 0x09
+#define MQTT_PROP_SUBSCRIPTION_IDENTIFIER 0x0B
+#define MQTT_PROP_SESSION_EXPIRY_INTERVAL 0x11
+#define MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER 0x12
+#define MQTT_PROP_SERVER_KEEP_ALIVE 0x13
+#define MQTT_PROP_AUTHENTICATION_METHOD 0x15
+#define MQTT_PROP_AUTHENTICATION_DATA 0x16
+#define MQTT_PROP_REQUEST_PROBLEM_INFORMATION 0x17
+#define MQTT_PROP_WILL_DELAY_INTERVAL 0x18
+#define MQTT_PROP_REQUEST_RESPONSE_INFORMATION 0x19
+#define MQTT_PROP_RESPONSE_INFORMATION 0x1A
+#define MQTT_PROP_SERVER_REFERENCE 0x1C
+#define MQTT_PROP_REASON_STRING 0x1F
+#define MQTT_PROP_RECEIVE_MAXIMUM 0x21
+#define MQTT_PROP_TOPIC_ALIAS_MAXIMUM 0x22
+#define MQTT_PROP_TOPIC_ALIAS 0x23
+#define MQTT_PROP_MAXIMUM_QOS 0x24
+#define MQTT_PROP_RETAIN_AVAILABLE 0x25
+#define MQTT_PROP_USER_PROPERTY 0x26
+#define MQTT_PROP_MAXIMUM_PACKET_SIZE 0x27
+#define MQTT_PROP_WILDCARD_SUBSCRIPTION_AVAILABLE 0x28
+#define MQTT_PROP_SUBSCRIPTION_IDENTIFIER_AVAILABLE 0x29
+#define MQTT_PROP_SHARED_SUBSCRIPTION_AVAILABLE 0x2A
+
+enum {
+ MQTT_PROP_TYPE_BYTE,
+ MQTT_PROP_TYPE_STRING,
+ MQTT_PROP_TYPE_STRING_PAIR,
+ MQTT_PROP_TYPE_BINARY_DATA,
+ MQTT_PROP_TYPE_VARIABLE_INT,
+ MQTT_PROP_TYPE_INT,
+ MQTT_PROP_TYPE_SHORT
+};
+
+enum { MQTT_OK, MQTT_INCOMPLETE, MQTT_MALFORMED };
+
+struct mg_mqtt_prop {
+ uint8_t id; // Enumerated at MQTT5 Reference
+ uint32_t iv; // Integer value for 8-, 16-, 32-bit integers types
+ struct mg_str key; // Non-NULL only for user property type
+ struct mg_str val; // Non-NULL only for UTF-8 types and user properties
+};
+
+struct mg_mqtt_opts {
+ struct mg_str user; // Username, can be empty
+ struct mg_str pass; // Password, can be empty
+ struct mg_str client_id; // Client ID
+ struct mg_str topic; // topic
+ struct mg_str message; // message
+ uint8_t qos; // message quality of service
+ uint8_t version; // Can be 4 (3.1.1), or 5. If 0, assume 4.
+ uint16_t keepalive; // Keep-alive timer in seconds
+ bool retain; // Retain last will
+ bool clean; // Use clean session, 0 or 1
+ struct mg_mqtt_prop *props; // MQTT5 props array
+ size_t num_props; // number of props
+ struct mg_mqtt_prop *will_props; // Valid only for CONNECT packet
+ size_t num_will_props; // Number of will props
+};
+
+struct mg_mqtt_message {
+ struct mg_str topic; // Parsed topic
+ struct mg_str data; // Parsed message
+ struct mg_str dgram; // Whole MQTT datagram, including headers
+ uint16_t id; // For PUBACK, PUBREC, PUBREL, PUBCOMP, SUBACK, PUBLISH
+ uint8_t cmd; // MQTT command, one of MQTT_CMD_*
+ uint8_t qos; // Quality of service
+ uint8_t ack; // Connack return code. 0 - success
+ size_t props_start; // Offset to the start of the properties
+ size_t props_size; // Length of the properties
+};
+
+struct mg_connection *mg_mqtt_connect(struct mg_mgr *, const char *url,
+ const struct mg_mqtt_opts *opts,
+ mg_event_handler_t fn, void *fn_data);
+struct mg_connection *mg_mqtt_listen(struct mg_mgr *mgr, const char *url,
+ mg_event_handler_t fn, void *fn_data);
+void mg_mqtt_login(struct mg_connection *c, const struct mg_mqtt_opts *opts);
+void mg_mqtt_pub(struct mg_connection *c, const struct mg_mqtt_opts *opts);
+void mg_mqtt_sub(struct mg_connection *, const struct mg_mqtt_opts *opts);
+int mg_mqtt_parse(const uint8_t *, size_t, uint8_t, struct mg_mqtt_message *);
+void mg_mqtt_send_header(struct mg_connection *, uint8_t cmd, uint8_t flags,
+ uint32_t len);
+void mg_mqtt_ping(struct mg_connection *);
+void mg_mqtt_pong(struct mg_connection *);
+void mg_mqtt_disconnect(struct mg_connection *, const struct mg_mqtt_opts *);
+size_t mg_mqtt_next_prop(struct mg_mqtt_message *, struct mg_mqtt_prop *,
+ size_t ofs);
+
+
+
+
+
+// Mongoose sends DNS queries that contain only one question:
+// either A (IPv4) or AAAA (IPv6) address lookup.
+// Therefore, we expect zero or one answer.
+// If `resolved` is true, then `addr` contains resolved IPv4 or IPV6 address.
+struct mg_dns_message {
+ uint16_t txnid; // Transaction ID
+ bool resolved; // Resolve successful, addr is set
+ struct mg_addr addr; // Resolved address
+ char name[256]; // Host name
+};
+
+struct mg_dns_header {
+ uint16_t txnid; // Transaction ID
+ uint16_t flags;
+ uint16_t num_questions;
+ uint16_t num_answers;
+ uint16_t num_authority_prs;
+ uint16_t num_other_prs;
+};
+
+// DNS resource record
+struct mg_dns_rr {
+ uint16_t nlen; // Name or pointer length
+ uint16_t atype; // Address type
+ uint16_t aclass; // Address class
+ uint16_t alen; // Address length
+};
+
+void mg_resolve(struct mg_connection *, const char *url);
+void mg_resolve_cancel(struct mg_connection *);
+bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *);
+size_t mg_dns_parse_rr(const uint8_t *buf, size_t len, size_t ofs,
+ bool is_question, struct mg_dns_rr *);
+
+
+
+
+
+#ifndef MG_JSON_MAX_DEPTH
+#define MG_JSON_MAX_DEPTH 30
+#endif
+
+// Error return values - negative. Successful returns are >= 0
+enum { MG_JSON_TOO_DEEP = -1, MG_JSON_INVALID = -2, MG_JSON_NOT_FOUND = -3 };
+int mg_json_get(struct mg_str json, const char *path, int *toklen);
+
+bool mg_json_get_num(struct mg_str json, const char *path, double *v);
+bool mg_json_get_bool(struct mg_str json, const char *path, bool *v);
+long mg_json_get_long(struct mg_str json, const char *path, long dflt);
+char *mg_json_get_str(struct mg_str json, const char *path);
+char *mg_json_get_hex(struct mg_str json, const char *path, int *len);
+char *mg_json_get_b64(struct mg_str json, const char *path, int *len);
+
+
+
+
+// JSON-RPC request descriptor
+struct mg_rpc_req {
+ struct mg_rpc **head; // RPC handlers list head
+ struct mg_rpc *rpc; // RPC handler being called
+ mg_pfn_t pfn; // Response printing function
+ void *pfn_data; // Response printing function data
+ void *req_data; // Arbitrary request data
+ struct mg_str frame; // Request, e.g. {"id":1,"method":"add","params":[1,2]}
+};
+
+// JSON-RPC method handler
+struct mg_rpc {
+ struct mg_rpc *next; // Next in list
+ struct mg_str method; // Method pattern
+ void (*fn)(struct mg_rpc_req *); // Handler function
+ void *fn_data; // Handler function argument
+};
+
+void mg_rpc_add(struct mg_rpc **head, struct mg_str method_pattern,
+ void (*handler)(struct mg_rpc_req *), void *handler_data);
+void mg_rpc_del(struct mg_rpc **head, void (*handler)(struct mg_rpc_req *));
+void mg_rpc_process(struct mg_rpc_req *);
+
+// Helper functions to print result or error frame
+void mg_rpc_ok(struct mg_rpc_req *, const char *fmt, ...);
+void mg_rpc_vok(struct mg_rpc_req *, const char *fmt, va_list *ap);
+void mg_rpc_err(struct mg_rpc_req *, int code, const char *fmt, ...);
+void mg_rpc_verr(struct mg_rpc_req *, int code, const char *fmt, va_list *);
+void mg_rpc_list(struct mg_rpc_req *r);
+
+
+#if MG_ENABLE_TCPIP
+
+
+
+
+struct mg_tcpip_if; // MIP network interface
+
+struct mg_tcpip_driver {
+ bool (*init)(struct mg_tcpip_if *); // Init driver
+ size_t (*tx)(const void *, size_t, struct mg_tcpip_if *); // Transmit frame
+ size_t (*rx)(void *buf, size_t len, struct mg_tcpip_if *); // Receive frame
+ bool (*up)(struct mg_tcpip_if *); // Up/down status
+};
+
+// Network interface
+struct mg_tcpip_if {
+ uint8_t mac[6]; // MAC address. Must be set to a valid MAC
+ uint32_t ip, mask, gw; // IP address, mask, default gateway
+ struct mg_str tx; // Output (TX) buffer
+ bool enable_dhcp_client; // Enable DCHP client
+ bool enable_dhcp_server; // Enable DCHP server
+ bool enable_crc32_check; // Do a CRC check on rx frames and strip it
+ bool enable_mac_check; // Do a MAC check on rx frames
+ struct mg_tcpip_driver *driver; // Low level driver
+ void *driver_data; // Driver-specific data
+ struct mg_mgr *mgr; // Mongoose event manager
+ struct mg_queue recv_queue; // Receive queue
+
+ // Internal state, user can use it but should not change it
+ uint8_t gwmac[6]; // Router's MAC
+ uint64_t now; // Current time
+ uint64_t timer_1000ms; // 1000 ms timer: for DHCP and link state
+ uint64_t lease_expire; // Lease expiration time
+ uint16_t eport; // Next ephemeral port
+ volatile uint32_t ndrop; // Number of received, but dropped frames
+ volatile uint32_t nrecv; // Number of received frames
+ volatile uint32_t nsent; // Number of transmitted frames
+ volatile uint32_t nerr; // Number of driver errors
+ uint8_t state; // Current state
+#define MG_TCPIP_STATE_DOWN 0 // Interface is down
+#define MG_TCPIP_STATE_UP 1 // Interface is up
+#define MG_TCPIP_STATE_READY 2 // Interface is up and has IP
+};
+
+void mg_tcpip_init(struct mg_mgr *, struct mg_tcpip_if *);
+void mg_tcpip_free(struct mg_tcpip_if *);
+void mg_tcpip_qwrite(void *buf, size_t len, struct mg_tcpip_if *ifp);
+
+extern struct mg_tcpip_driver mg_tcpip_driver_stm32;
+extern struct mg_tcpip_driver mg_tcpip_driver_w5500;
+extern struct mg_tcpip_driver mg_tcpip_driver_tm4c;
+extern struct mg_tcpip_driver mg_tcpip_driver_stm32h;
+extern struct mg_tcpip_driver mg_tcpip_driver_imxrt;
+
+// Drivers that require SPI, can use this SPI abstraction
+struct mg_tcpip_spi {
+ void *spi; // Opaque SPI bus descriptor
+ void (*begin)(void *); // SPI begin: slave select low
+ void (*end)(void *); // SPI end: slave select high
+ uint8_t (*txn)(void *, uint8_t); // SPI transaction: write 1 byte, read reply
+};
+
+#if !defined(MG_ENABLE_DRIVER_STM32H) && !defined(MG_ENABLE_DRIVER_TM4C)
+#define MG_ENABLE_DRIVER_STM32 1
+#else
+#define MG_ENABLE_DRIVER_STM32 0
+#endif
+#endif
+
+
+struct mg_tcpip_driver_imxrt1020_data {
+ // MDC clock divider. MDC clock is derived from IPS Bus clock (ipg_clk),
+ // must not exceed 2.5MHz. Configuration for clock range 2.36~2.50 MHz
+ // ipg_clk MSCR mdc_cr VALUE
+ // -------------------------------------
+ // -1 <-- tell driver to guess the value
+ // 25 MHz 0x04 0
+ // 33 MHz 0x06 1
+ // 40 MHz 0x07 2
+ // 50 MHz 0x09 3
+ // 66 MHz 0x0D 4 <-- value for iMXRT1020-EVK at max freq.
+ int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4
+};
+
+
+struct mg_tcpip_driver_stm32_data {
+ // MDC clock divider. MDC clock is derived from HCLK, must not exceed 2.5MHz
+ // HCLK range DIVIDER mdc_cr VALUE
+ // -------------------------------------
+ // -1 <-- tell driver to guess the value
+ // 60-100 MHz HCLK/42 0
+ // 100-150 MHz HCLK/62 1
+ // 20-35 MHz HCLK/16 2
+ // 35-60 MHz HCLK/26 3
+ // 150-216 MHz HCLK/102 4 <-- value for Nucleo-F* on max speed
+ // 216-310 MHz HCLK/124 5
+ // 110, 111 Reserved
+ int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5
+};
+
+
+struct mg_tcpip_driver_stm32h_data {
+ // MDC clock divider. MDC clock is derived from HCLK, must not exceed 2.5MHz
+ // HCLK range DIVIDER mdc_cr VALUE
+ // -------------------------------------
+ // -1 <-- tell driver to guess the value
+ // 60-100 MHz HCLK/42 0
+ // 100-150 MHz HCLK/62 1
+ // 20-35 MHz HCLK/16 2
+ // 35-60 MHz HCLK/26 3
+ // 150-250 MHz HCLK/102 4 <-- value for Nucleo-H* on max speed driven by HSI
+ // 250-300 MHz HCLK/124 5 <-- value for Nucleo-H* on max speed driven by CSI
+ // 110, 111 Reserved
+ int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5
+};
+
+
+struct mg_tcpip_driver_tm4c_data {
+ // MDC clock divider. MDC clock is derived from SYSCLK, must not exceed 2.5MHz
+ // SYSCLK range DIVIDER mdc_cr VALUE
+ // -------------------------------------
+ // -1 <-- tell driver to guess the value
+ // 60-100 MHz SYSCLK/42 0
+ // 100-150 MHz SYSCLK/62 1 <-- value for EK-TM4C129* on max speed
+ // 20-35 MHz SYSCLK/16 2
+ // 35-60 MHz SYSCLK/26 3
+ // 0x4-0xF Reserved
+ int mdc_cr; // Valid values: -1, 0, 1, 2, 3
+};
+
+#ifdef __cplusplus
+}
+#endif
+#endif // MONGOOSE_H
--- /dev/null
+/* Copyright 2015 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef OLM_ACCOUNT_HH_
+#define OLM_ACCOUNT_HH_
+
+#include "olm/list.hh"
+#include "olm/crypto.h"
+#include "olm/error.h"
+
+#include <cstdint>
+
+namespace olm {
+
+
+struct IdentityKeys {
+ _olm_ed25519_key_pair ed25519_key;
+ _olm_curve25519_key_pair curve25519_key;
+};
+
+struct OneTimeKey {
+ std::uint32_t id;
+ bool published;
+ _olm_curve25519_key_pair key;
+};
+
+
+static std::size_t const MAX_ONE_TIME_KEYS = 100;
+
+
+struct Account {
+ Account();
+ IdentityKeys identity_keys;
+ List<OneTimeKey, MAX_ONE_TIME_KEYS> one_time_keys;
+ std::uint8_t num_fallback_keys;
+ OneTimeKey current_fallback_key;
+ OneTimeKey prev_fallback_key;
+ std::uint32_t next_one_time_key_id;
+ OlmErrorCode last_error;
+
+ /** Number of random bytes needed to create a new account */
+ std::size_t new_account_random_length() const;
+
+ /** Create a new account. Returns std::size_t(-1) on error. If the number of
+ * random bytes is too small then last_error will be NOT_ENOUGH_RANDOM */
+ std::size_t new_account(
+ uint8_t const * random, std::size_t random_length
+ );
+
+ /** Number of bytes needed to output the identity keys for this account */
+ std::size_t get_identity_json_length() const;
+
+ /** Output the identity keys for this account as JSON in the following
+ * format:
+ *
+ * {"curve25519":"<43 base64 characters>"
+ * ,"ed25519":"<43 base64 characters>"
+ * }
+ *
+ *
+ * Returns the size of the JSON written or std::size_t(-1) on error.
+ * If the buffer is too small last_error will be OUTPUT_BUFFER_TOO_SMALL. */
+ std::size_t get_identity_json(
+ std::uint8_t * identity_json, std::size_t identity_json_length
+ );
+
+ /**
+ * The length of an ed25519 signature in bytes.
+ */
+ std::size_t signature_length() const;
+
+ /**
+ * Signs a message with the ed25519 key for this account.
+ */
+ std::size_t sign(
+ std::uint8_t const * message, std::size_t message_length,
+ std::uint8_t * signature, std::size_t signature_length
+ );
+
+ /** Number of bytes needed to output the one time keys for this account */
+ std::size_t get_one_time_keys_json_length() const;
+
+ /** Output the one time keys that haven't been published yet as JSON:
+ *
+ * {"curve25519":
+ * ["<6 byte key id>":"<43 base64 characters>"
+ * ,"<6 byte key id>":"<43 base64 characters>"
+ * ...
+ * ]
+ * }
+ *
+ * Returns the size of the JSON written or std::size_t(-1) on error.
+ * If the buffer is too small last_error will be OUTPUT_BUFFER_TOO_SMALL.
+ */
+ std::size_t get_one_time_keys_json(
+ std::uint8_t * one_time_json, std::size_t one_time_json_length
+ );
+
+ /** Mark the current list of one_time_keys and the current fallback key as
+ * being published. The current one time keys will no longer be returned by
+ * get_one_time_keys_json() and the current fallback key will no longer be
+ * returned by get_unpublished_fallback_key_json(). */
+ std::size_t mark_keys_as_published();
+
+ /** The largest number of one time keys this account can store. */
+ std::size_t max_number_of_one_time_keys() const;
+
+ /** The number of random bytes needed to generate a given number of new one
+ * time keys. */
+ std::size_t generate_one_time_keys_random_length(
+ std::size_t number_of_keys
+ ) const;
+
+ /** Generates a number of new one time keys. If the total number of keys
+ * stored by this account exceeds max_number_of_one_time_keys() then the
+ * old keys are discarded. Returns std::size_t(-1) on error. If the number
+ * of random bytes is too small then last_error will be NOT_ENOUGH_RANDOM */
+ std::size_t generate_one_time_keys(
+ std::size_t number_of_keys,
+ std::uint8_t const * random, std::size_t random_length
+ );
+
+ /** The number of random bytes needed to generate a fallback key. */
+ std::size_t generate_fallback_key_random_length() const;
+
+ /** Generates a new fallback key. Returns std::size_t(-1) on error. If the
+ * number of random bytes is too small then last_error will be
+ * NOT_ENOUGH_RANDOM */
+ std::size_t generate_fallback_key(
+ std::uint8_t const * random, std::size_t random_length
+ );
+
+ /** Number of bytes needed to output the fallback keys for this account */
+ std::size_t get_fallback_key_json_length() const;
+
+ /** Deprecated: use get_unpublished_fallback_key_json instead */
+ std::size_t get_fallback_key_json(
+ std::uint8_t * fallback_json, std::size_t fallback_json_length
+ );
+
+ /** Number of bytes needed to output the unpublished fallback keys for this
+ * account */
+ std::size_t get_unpublished_fallback_key_json_length() const;
+
+ /** Output the fallback key as JSON:
+ *
+ * {"curve25519":
+ * ["<6 byte key id>":"<43 base64 characters>"
+ * ,"<6 byte key id>":"<43 base64 characters>"
+ * ...
+ * ]
+ * }
+ *
+ * if there is a fallback key and it has not been published yet.
+ *
+ * Returns the size of the JSON written or std::size_t(-1) on error.
+ * If the buffer is too small last_error will be OUTPUT_BUFFER_TOO_SMALL.
+ */
+ std::size_t get_unpublished_fallback_key_json(
+ std::uint8_t * fallback_json, std::size_t fallback_json_length
+ );
+
+ /** Forget about the old fallback key */
+ void forget_old_fallback_key();
+
+ /** Lookup a one time key with the given public key */
+ OneTimeKey const * lookup_key(
+ _olm_curve25519_public_key const & public_key
+ );
+
+ /** Remove a one time key with the given public key */
+ std::size_t remove_key(
+ _olm_curve25519_public_key const & public_key
+ );
+};
+
+
+std::size_t pickle_length(
+ Account const & value
+);
+
+
+std::uint8_t * pickle(
+ std::uint8_t * pos,
+ Account const & value
+);
+
+
+std::uint8_t const * unpickle(
+ std::uint8_t const * pos, std::uint8_t const * end,
+ Account & value
+);
+
+
+} // namespace olm
+
+#endif /* OLM_ACCOUNT_HH_ */
--- /dev/null
+/* Copyright 2015, 2016 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* C bindings for base64 functions */
+
+
+#ifndef OLM_BASE64_H_
+#define OLM_BASE64_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+// Note: exports in this file are only for unit tests. Nobody else should be
+// using this externally
+#include "olm/olm_export.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * The number of bytes of unpadded base64 needed to encode a length of input.
+ */
+OLM_EXPORT size_t _olm_encode_base64_length(
+ size_t input_length
+);
+
+/**
+ * Encode the raw input as unpadded base64.
+ * Writes encode_base64_length(input_length) bytes to the output buffer.
+ * The input can overlap with the last three quarters of the output buffer.
+ * That is, the input pointer may be output + output_length - input_length.
+ *
+ * Returns number of bytes encoded
+ */
+OLM_EXPORT size_t _olm_encode_base64(
+ uint8_t const * input, size_t input_length,
+ uint8_t * output
+);
+
+/**
+ * The number of bytes of raw data a length of unpadded base64 will encode to.
+ * Returns size_t(-1) if the length is not a valid length for base64.
+ */
+OLM_EXPORT size_t _olm_decode_base64_length(
+ size_t input_length
+);
+
+/**
+ * Decodes the unpadded base64 input to raw bytes.
+ * Writes decode_base64_length(input_length) bytes to the output buffer.
+ * The output can overlap with the first three quarters of the input buffer.
+ * That is, the input pointers and output pointer may be the same.
+ *
+ * Returns number of bytes decoded
+ */
+OLM_EXPORT size_t _olm_decode_base64(
+ uint8_t const * input, size_t input_length,
+ uint8_t * output
+);
+
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+
+#endif /* OLM_BASE64_H_ */
--- /dev/null
+/* Copyright 2015 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef OLM_BASE64_HH_
+#define OLM_BASE64_HH_
+
+#include <cstddef>
+#include <cstdint>
+
+// Note: exports in this file are only for unit tests. Nobody else should be
+// using this externally
+#include "olm/olm_export.h"
+
+namespace olm {
+
+/**
+ * The number of bytes of unpadded base64 needed to encode a length of input.
+ */
+OLM_EXPORT std::size_t encode_base64_length(
+ std::size_t input_length
+);
+
+/**
+ * Encode the raw input as unpadded base64.
+ * Writes encode_base64_length(input_length) bytes to the output buffer.
+ * The input can overlap with the last three quarters of the output buffer.
+ * That is, the input pointer may be output + output_length - input_length.
+ */
+OLM_EXPORT std::uint8_t * encode_base64(
+ std::uint8_t const * input, std::size_t input_length,
+ std::uint8_t * output
+);
+
+/**
+ * The number of bytes of raw data a length of unpadded base64 will encode to.
+ * Returns std::size_t(-1) if the length is not a valid length for base64.
+ */
+OLM_EXPORT std::size_t decode_base64_length(
+ std::size_t input_length
+);
+
+/**
+ * Decodes the unpadded base64 input to raw bytes.
+ * Writes decode_base64_length(input_length) bytes to the output buffer.
+ * The output can overlap with the first three quarters of the input buffer.
+ * That is, the input pointers and output pointer may be the same.
+ *
+ * Returns the number of bytes of raw data the base64 input decoded to. If the
+ * input length supplied is not a valid length for base64, returns
+ * std::size_t(-1) and does not decode.
+ */
+OLM_EXPORT std::size_t decode_base64(
+ std::uint8_t const * input, std::size_t input_length,
+ std::uint8_t * output
+);
+
+} // namespace olm
+
+
+#endif /* OLM_BASE64_HH_ */
--- /dev/null
+/* Copyright 2015 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OLM_CIPHER_H_
+#define OLM_CIPHER_H_
+
+#include <stdint.h>
+#include <stdlib.h>
+
+// Note: exports in this file are only for unit tests. Nobody else should be
+// using this externally
+#include "olm/olm_export.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _olm_cipher;
+
+struct _olm_cipher_ops {
+ /**
+ * Returns the length of the message authentication code that will be
+ * appended to the output.
+ */
+ size_t (*mac_length)(const struct _olm_cipher *cipher);
+
+ /**
+ * Returns the length of cipher-text for a given length of plain-text.
+ */
+ size_t (*encrypt_ciphertext_length)(
+ const struct _olm_cipher *cipher,
+ size_t plaintext_length
+ );
+
+ /*
+ * Encrypts the plain-text into the output buffer and authenticates the
+ * contents of the output buffer covering both cipher-text and any other
+ * associated data in the output buffer.
+ *
+ * |---------------------------------------output_length-->|
+ * output |--ciphertext_length-->| |---mac_length-->|
+ * ciphertext
+ *
+ * The plain-text pointers and cipher-text pointers may be the same.
+ *
+ * Returns size_t(-1) if the length of the cipher-text or the output
+ * buffer is too small. Otherwise returns the length of the output buffer.
+ */
+ size_t (*encrypt)(
+ const struct _olm_cipher *cipher,
+ uint8_t const * key, size_t key_length,
+ uint8_t const * plaintext, size_t plaintext_length,
+ uint8_t * ciphertext, size_t ciphertext_length,
+ uint8_t * output, size_t output_length
+ );
+
+ /**
+ * Returns the maximum length of plain-text that a given length of
+ * cipher-text can contain.
+ */
+ size_t (*decrypt_max_plaintext_length)(
+ const struct _olm_cipher *cipher,
+ size_t ciphertext_length
+ );
+
+ /**
+ * Authenticates the input and decrypts the cipher-text into the plain-text
+ * buffer.
+ *
+ * |----------------------------------------input_length-->|
+ * input |--ciphertext_length-->| |---mac_length-->|
+ * ciphertext
+ *
+ * The plain-text pointers and cipher-text pointers may be the same.
+ *
+ * Returns size_t(-1) if the length of the plain-text buffer is too
+ * small or if the authentication check fails. Otherwise returns the length
+ * of the plain text.
+ */
+ size_t (*decrypt)(
+ const struct _olm_cipher *cipher,
+ uint8_t const * key, size_t key_length,
+ uint8_t const * input, size_t input_length,
+ uint8_t const * ciphertext, size_t ciphertext_length,
+ uint8_t * plaintext, size_t max_plaintext_length
+ );
+};
+
+struct _olm_cipher {
+ const struct _olm_cipher_ops *ops;
+ /* cipher-specific fields follow */
+};
+
+struct _olm_cipher_aes_sha_256 {
+ struct _olm_cipher base_cipher;
+
+ /** context string for the HKDF used for deriving the AES256 key, HMAC key,
+ * and AES IV, from the key material passed to encrypt/decrypt.
+ */
+ uint8_t const * kdf_info;
+
+ /** length of context string kdf_info */
+ size_t kdf_info_length;
+};
+
+OLM_EXPORT extern const struct _olm_cipher_ops _olm_cipher_aes_sha_256_ops;
+
+/**
+ * get an initializer for an instance of struct _olm_cipher_aes_sha_256.
+ *
+ * To use it, declare:
+ *
+ * struct _olm_cipher_aes_sha_256 MY_CIPHER =
+ * OLM_CIPHER_INIT_AES_SHA_256("MY_KDF");
+ * struct _olm_cipher *cipher = OLM_CIPHER_BASE(&MY_CIPHER);
+ */
+#define OLM_CIPHER_INIT_AES_SHA_256(KDF_INFO) { \
+ /*.base_cipher = */{ &_olm_cipher_aes_sha_256_ops },\
+ /*.kdf_info = */(uint8_t *)(KDF_INFO), \
+ /*.kdf_info_length = */sizeof(KDF_INFO) - 1 \
+}
+#define OLM_CIPHER_BASE(CIPHER) \
+ (&((CIPHER)->base_cipher))
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* OLM_CIPHER_H_ */
--- /dev/null
+/* Copyright 2015 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* C-compatible crpyto utility functions. At some point all of crypto.hh will
+ * move here.
+ */
+
+#ifndef OLM_CRYPTO_H_
+#define OLM_CRYPTO_H_
+
+// Note: exports in this file are only for unit tests. Nobody else should be
+// using this externally
+#include "olm/olm_export.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** length of a sha256 hash */
+#define SHA256_OUTPUT_LENGTH 32
+
+/** length of a public or private Curve25519 key */
+#define CURVE25519_KEY_LENGTH 32
+
+/** length of the shared secret created by a Curve25519 ECDH operation */
+#define CURVE25519_SHARED_SECRET_LENGTH 32
+
+/** amount of random data required to create a Curve25519 keypair */
+#define CURVE25519_RANDOM_LENGTH CURVE25519_KEY_LENGTH
+
+/** length of a public Ed25519 key */
+#define ED25519_PUBLIC_KEY_LENGTH 32
+
+/** length of a private Ed25519 key */
+#define ED25519_PRIVATE_KEY_LENGTH 64
+
+/** amount of random data required to create a Ed25519 keypair */
+#define ED25519_RANDOM_LENGTH 32
+
+/** length of an Ed25519 signature */
+#define ED25519_SIGNATURE_LENGTH 64
+
+/** length of an aes256 key */
+#define AES256_KEY_LENGTH 32
+
+/** length of an aes256 initialisation vector */
+#define AES256_IV_LENGTH 16
+
+struct _olm_aes256_key {
+ uint8_t key[AES256_KEY_LENGTH];
+};
+
+struct _olm_aes256_iv {
+ uint8_t iv[AES256_IV_LENGTH];
+};
+
+
+struct _olm_curve25519_public_key {
+ uint8_t public_key[CURVE25519_KEY_LENGTH];
+};
+
+struct _olm_curve25519_private_key {
+ uint8_t private_key[CURVE25519_KEY_LENGTH];
+};
+
+struct _olm_curve25519_key_pair {
+ struct _olm_curve25519_public_key public_key;
+ struct _olm_curve25519_private_key private_key;
+};
+
+struct _olm_ed25519_public_key {
+ uint8_t public_key[ED25519_PUBLIC_KEY_LENGTH];
+};
+
+struct _olm_ed25519_private_key {
+ uint8_t private_key[ED25519_PRIVATE_KEY_LENGTH];
+};
+
+struct _olm_ed25519_key_pair {
+ struct _olm_ed25519_public_key public_key;
+ struct _olm_ed25519_private_key private_key;
+};
+
+
+/** The length of output the aes_encrypt_cbc function will write */
+OLM_EXPORT size_t _olm_crypto_aes_encrypt_cbc_length(
+ size_t input_length
+);
+
+/** Encrypts the input using AES256 in CBC mode with PKCS#7 padding.
+ * The output buffer must be big enough to hold the output including padding */
+OLM_EXPORT void _olm_crypto_aes_encrypt_cbc(
+ const struct _olm_aes256_key *key,
+ const struct _olm_aes256_iv *iv,
+ const uint8_t *input, size_t input_length,
+ uint8_t *output
+);
+
+/** Decrypts the input using AES256 in CBC mode. The output buffer must be at
+ * least the same size as the input buffer. Returns the length of the plaintext
+ * without padding on success or std::size_t(-1) if the padding is invalid.
+ */
+OLM_EXPORT size_t _olm_crypto_aes_decrypt_cbc(
+ const struct _olm_aes256_key *key,
+ const struct _olm_aes256_iv *iv,
+ uint8_t const * input, size_t input_length,
+ uint8_t * output
+);
+
+
+/** Computes SHA-256 of the input. The output buffer must be a least
+ * SHA256_OUTPUT_LENGTH (32) bytes long. */
+OLM_EXPORT void _olm_crypto_sha256(
+ uint8_t const * input, size_t input_length,
+ uint8_t * output
+);
+
+/** HMAC: Keyed-Hashing for Message Authentication
+ * http://tools.ietf.org/html/rfc2104
+ * Computes HMAC-SHA-256 of the input for the key. The output buffer must
+ * be at least SHA256_OUTPUT_LENGTH (32) bytes long. */
+OLM_EXPORT void _olm_crypto_hmac_sha256(
+ uint8_t const * key, size_t key_length,
+ uint8_t const * input, size_t input_length,
+ uint8_t * output
+);
+
+
+/** HMAC-based Key Derivation Function (HKDF)
+ * https://tools.ietf.org/html/rfc5869
+ * Derives key material from the input bytes. */
+OLM_EXPORT void _olm_crypto_hkdf_sha256(
+ uint8_t const * input, size_t input_length,
+ uint8_t const * info, size_t info_length,
+ uint8_t const * salt, size_t salt_length,
+ uint8_t * output, size_t output_length
+);
+
+
+/** Generate a curve25519 key pair
+ * random_32_bytes should be CURVE25519_RANDOM_LENGTH (32) bytes long.
+ */
+OLM_EXPORT void _olm_crypto_curve25519_generate_key(
+ uint8_t const * random_32_bytes,
+ struct _olm_curve25519_key_pair *output
+);
+
+
+/** Create a shared secret using our private key and their public key.
+ * The output buffer must be at least CURVE25519_SHARED_SECRET_LENGTH (32) bytes long.
+ */
+OLM_EXPORT void _olm_crypto_curve25519_shared_secret(
+ const struct _olm_curve25519_key_pair *our_key,
+ const struct _olm_curve25519_public_key *their_key,
+ uint8_t * output
+);
+
+/** Generate an ed25519 key pair
+ * random_32_bytes should be ED25519_RANDOM_LENGTH (32) bytes long.
+ */
+OLM_EXPORT void _olm_crypto_ed25519_generate_key(
+ uint8_t const * random_bytes,
+ struct _olm_ed25519_key_pair *output
+);
+
+/** Signs the message using our private key.
+ *
+ * The output buffer must be at least ED25519_SIGNATURE_LENGTH (64) bytes
+ * long. */
+OLM_EXPORT void _olm_crypto_ed25519_sign(
+ const struct _olm_ed25519_key_pair *our_key,
+ const uint8_t * message, size_t message_length,
+ uint8_t * output
+);
+
+/** Verify an ed25519 signature
+ * The signature input buffer must be ED25519_SIGNATURE_LENGTH (64) bytes long.
+ * Returns non-zero if the signature is valid. */
+OLM_EXPORT int _olm_crypto_ed25519_verify(
+ const struct _olm_ed25519_public_key *their_key,
+ const uint8_t * message, size_t message_length,
+ const uint8_t * signature
+);
+
+
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* OLM_CRYPTO_H_ */
--- /dev/null
+/* Copyright 2015-2016 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef OLM_ERROR_H_
+#define OLM_ERROR_H_
+
+#include "olm/olm_export.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum OlmErrorCode {
+ OLM_SUCCESS = 0, /*!< There wasn't an error */
+ OLM_NOT_ENOUGH_RANDOM = 1, /*!< Not enough entropy was supplied */
+ OLM_OUTPUT_BUFFER_TOO_SMALL = 2, /*!< Supplied output buffer is too small */
+ OLM_BAD_MESSAGE_VERSION = 3, /*!< The message version is unsupported */
+ OLM_BAD_MESSAGE_FORMAT = 4, /*!< The message couldn't be decoded */
+ OLM_BAD_MESSAGE_MAC = 5, /*!< The message couldn't be decrypted */
+ OLM_BAD_MESSAGE_KEY_ID = 6, /*!< The message references an unknown key id */
+ OLM_INVALID_BASE64 = 7, /*!< The input base64 was invalid */
+ OLM_BAD_ACCOUNT_KEY = 8, /*!< The supplied account key is invalid */
+ OLM_UNKNOWN_PICKLE_VERSION = 9, /*!< The pickled object is too new */
+ OLM_CORRUPTED_PICKLE = 10, /*!< The pickled object couldn't be decoded */
+
+ OLM_BAD_SESSION_KEY = 11, /*!< Attempt to initialise an inbound group
+ session from an invalid session key */
+ OLM_UNKNOWN_MESSAGE_INDEX = 12, /*!< Attempt to decode a message whose
+ * index is earlier than our earliest
+ * known session key.
+ */
+
+ /**
+ * Attempt to unpickle an account which uses pickle version 1 (which did
+ * not save enough space for the Ed25519 key; the key should be considered
+ * compromised. We don't let the user reload the account.
+ */
+ OLM_BAD_LEGACY_ACCOUNT_PICKLE = 13,
+
+ /**
+ * Received message had a bad signature
+ */
+ OLM_BAD_SIGNATURE = 14,
+
+ OLM_INPUT_BUFFER_TOO_SMALL = 15,
+
+ /**
+ * SAS doesn't have their key set.
+ */
+ OLM_SAS_THEIR_KEY_NOT_SET = 16,
+
+ /**
+ * The pickled object was successfully decoded, but the unpickling still failed
+ * because it had some extraneous junk data at the end.
+ */
+ OLM_PICKLE_EXTRA_DATA = 17,
+
+ /* remember to update the list of string constants in error.c when updating
+ * this list. */
+};
+
+/** get a string representation of the given error code. */
+OLM_EXPORT const char * _olm_error_to_string(enum OlmErrorCode error);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* OLM_ERROR_H_ */
--- /dev/null
+/* Copyright 2016 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef OLM_INBOUND_GROUP_SESSION_H_
+#define OLM_INBOUND_GROUP_SESSION_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "olm/error.h"
+
+#include "olm/olm_export.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct OlmInboundGroupSession OlmInboundGroupSession;
+
+/** get the size of an inbound group session, in bytes. */
+OLM_EXPORT size_t olm_inbound_group_session_size(void);
+
+/**
+ * Initialise an inbound group session object using the supplied memory
+ * The supplied memory should be at least olm_inbound_group_session_size()
+ * bytes.
+ */
+OLM_EXPORT OlmInboundGroupSession * olm_inbound_group_session(
+ void *memory
+);
+
+/**
+ * A null terminated string describing the most recent error to happen to a
+ * group session */
+OLM_EXPORT const char *olm_inbound_group_session_last_error(
+ const OlmInboundGroupSession *session
+);
+
+/**
+ * An error code describing the most recent error to happen to a group
+ * session */
+OLM_EXPORT enum OlmErrorCode olm_inbound_group_session_last_error_code(
+ const OlmInboundGroupSession *session
+);
+
+/** Clears the memory used to back this group session */
+OLM_EXPORT size_t olm_clear_inbound_group_session(
+ OlmInboundGroupSession *session
+);
+
+/** Returns the number of bytes needed to store an inbound group session */
+OLM_EXPORT size_t olm_pickle_inbound_group_session_length(
+ const OlmInboundGroupSession *session
+);
+
+/**
+ * Stores a group session as a base64 string. Encrypts the session using the
+ * supplied key. Returns the length of the session on success.
+ *
+ * Returns olm_error() on failure. If the pickle output buffer
+ * is smaller than olm_pickle_inbound_group_session_length() then
+ * olm_inbound_group_session_last_error() will be "OUTPUT_BUFFER_TOO_SMALL"
+ */
+OLM_EXPORT size_t olm_pickle_inbound_group_session(
+ OlmInboundGroupSession *session,
+ void const * key, size_t key_length,
+ void * pickled, size_t pickled_length
+);
+
+/**
+ * Loads a group session from a pickled base64 string. Decrypts the session
+ * using the supplied key.
+ *
+ * Returns olm_error() on failure. If the key doesn't match the one used to
+ * encrypt the account then olm_inbound_group_session_last_error() will be
+ * "BAD_ACCOUNT_KEY". If the base64 couldn't be decoded then
+ * olm_inbound_group_session_last_error() will be "INVALID_BASE64". The input
+ * pickled buffer is destroyed
+ */
+OLM_EXPORT size_t olm_unpickle_inbound_group_session(
+ OlmInboundGroupSession *session,
+ void const * key, size_t key_length,
+ void * pickled, size_t pickled_length
+);
+
+
+/**
+ * Start a new inbound group session, from a key exported from
+ * olm_outbound_group_session_key
+ *
+ * Returns olm_error() on failure. On failure last_error will be set with an
+ * error code. The last_error will be:
+ *
+ * * OLM_INVALID_BASE64 if the session_key is not valid base64
+ * * OLM_BAD_SESSION_KEY if the session_key is invalid
+ */
+OLM_EXPORT size_t olm_init_inbound_group_session(
+ OlmInboundGroupSession *session,
+ /* base64-encoded keys */
+ uint8_t const * session_key, size_t session_key_length
+);
+
+/**
+ * Import an inbound group session, from a previous export.
+ *
+ * Returns olm_error() on failure. On failure last_error will be set with an
+ * error code. The last_error will be:
+ *
+ * * OLM_INVALID_BASE64 if the session_key is not valid base64
+ * * OLM_BAD_SESSION_KEY if the session_key is invalid
+ */
+OLM_EXPORT size_t olm_import_inbound_group_session(
+ OlmInboundGroupSession *session,
+ /* base64-encoded keys; note that it will be overwritten with the base64-decoded
+ data. */
+ uint8_t const * session_key, size_t session_key_length
+);
+
+
+/**
+ * Get an upper bound on the number of bytes of plain-text the decrypt method
+ * will write for a given input message length. The actual size could be
+ * different due to padding.
+ *
+ * The input message buffer is destroyed.
+ *
+ * Returns olm_error() on failure.
+ */
+OLM_EXPORT size_t olm_group_decrypt_max_plaintext_length(
+ OlmInboundGroupSession *session,
+ uint8_t * message, size_t message_length
+);
+
+/**
+ * Decrypt a message.
+ *
+ * The input message buffer is destroyed.
+ *
+ * Returns the length of the decrypted plain-text, or olm_error() on failure.
+ *
+ * On failure last_error will be set with an error code. The last_error will
+ * be:
+ * * OLM_OUTPUT_BUFFER_TOO_SMALL if the plain-text buffer is too small
+ * * OLM_INVALID_BASE64 if the message is not valid base-64
+ * * OLM_BAD_MESSAGE_VERSION if the message was encrypted with an unsupported
+ * version of the protocol
+ * * OLM_BAD_MESSAGE_FORMAT if the message headers could not be decoded
+ * * OLM_BAD_MESSAGE_MAC if the message could not be verified
+ * * OLM_UNKNOWN_MESSAGE_INDEX if we do not have a session key corresponding to the
+ * message's index (ie, it was sent before the session key was shared with
+ * us)
+ */
+OLM_EXPORT size_t olm_group_decrypt(
+ OlmInboundGroupSession *session,
+
+ /* input; note that it will be overwritten with the base64-decoded
+ message. */
+ uint8_t * message, size_t message_length,
+
+ /* output */
+ uint8_t * plaintext, size_t max_plaintext_length,
+ uint32_t * message_index
+);
+
+
+/**
+ * Get the number of bytes returned by olm_inbound_group_session_id()
+ */
+OLM_EXPORT size_t olm_inbound_group_session_id_length(
+ const OlmInboundGroupSession *session
+);
+
+/**
+ * Get a base64-encoded identifier for this session.
+ *
+ * Returns the length of the session id on success or olm_error() on
+ * failure. On failure last_error will be set with an error code. The
+ * last_error will be OUTPUT_BUFFER_TOO_SMALL if the id buffer was too
+ * small.
+ */
+OLM_EXPORT size_t olm_inbound_group_session_id(
+ OlmInboundGroupSession *session,
+ uint8_t * id, size_t id_length
+);
+
+/**
+ * Get the first message index we know how to decrypt.
+ */
+OLM_EXPORT uint32_t olm_inbound_group_session_first_known_index(
+ const OlmInboundGroupSession *session
+);
+
+
+/**
+ * Check if the session has been verified as a valid session.
+ *
+ * (A session is verified either because the original session share was signed,
+ * or because we have subsequently successfully decrypted a message.)
+ *
+ * This is mainly intended for the unit tests, currently.
+ */
+OLM_EXPORT int olm_inbound_group_session_is_verified(
+ const OlmInboundGroupSession *session
+);
+
+/**
+ * Get the number of bytes returned by olm_export_inbound_group_session()
+ */
+OLM_EXPORT size_t olm_export_inbound_group_session_length(
+ const OlmInboundGroupSession *session
+);
+
+/**
+ * Export the base64-encoded ratchet key for this session, at the given index,
+ * in a format which can be used by olm_import_inbound_group_session
+ *
+ * Returns the length of the ratchet key on success or olm_error() on
+ * failure. On failure last_error will be set with an error code. The
+ * last_error will be:
+ * * OUTPUT_BUFFER_TOO_SMALL if the buffer was too small
+ * * OLM_UNKNOWN_MESSAGE_INDEX if we do not have a session key corresponding to the
+ * given index (ie, it was sent before the session key was shared with
+ * us)
+ */
+OLM_EXPORT size_t olm_export_inbound_group_session(
+ OlmInboundGroupSession *session,
+ uint8_t * key, size_t key_length, uint32_t message_index
+);
+
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* OLM_INBOUND_GROUP_SESSION_H_ */
--- /dev/null
+/* Copyright 2015 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef OLM_LIST_HH_
+#define OLM_LIST_HH_
+
+#include <cstddef>
+
+namespace olm {
+
+template<typename T, std::size_t max_size>
+class List {
+public:
+ List() : _end(_data) {}
+
+ typedef T * iterator;
+ typedef T const * const_iterator;
+
+ T * begin() { return _data; }
+ T * end() { return _end; }
+ T const * begin() const { return _data; }
+ T const * end() const { return _end; }
+
+ /**
+ * Is the list empty?
+ */
+ bool empty() const { return _end == _data; }
+
+ /**
+ * The number of items in the list.
+ */
+ std::size_t size() const { return _end - _data; }
+
+ T & operator[](std::size_t index) { return _data[index]; }
+
+ T const & operator[](std::size_t index) const { return _data[index]; }
+
+ /**
+ * Erase the item from the list at the given position.
+ */
+ void erase(T * pos) {
+ --_end;
+ while (pos != _end) {
+ *pos = *(pos + 1);
+ ++pos;
+ }
+ }
+
+ /**
+ * Make space for an item in the list at a given position.
+ * If inserting the item makes the list longer than max_size then
+ * the end of the list is discarded.
+ * Returns the where the item is inserted.
+ */
+ T * insert(T * pos) {
+ if (_end != _data + max_size) {
+ ++_end;
+ } else if (pos == _end) {
+ --pos;
+ }
+ T * tmp = _end - 1;
+ while (tmp != pos) {
+ *tmp = *(tmp - 1);
+ --tmp;
+ }
+ return pos;
+ }
+
+ /**
+ * Make space for an item in the list at the start of the list
+ */
+ T * insert() { return insert(begin()); }
+
+ /**
+ * Insert an item into the list at a given position.
+ * If inserting the item makes the list longer than max_size then
+ * the end of the list is discarded.
+ * Returns the where the item is inserted.
+ */
+ T * insert(T * pos, T const & value) {
+ pos = insert(pos);
+ *pos = value;
+ return pos;
+ }
+
+ List<T, max_size> & operator=(List<T, max_size> const & other) {
+ if (this == &other) {
+ return *this;
+ }
+ T * this_pos = _data;
+ T * const other_pos = other._data;
+ while (other_pos != other._end) {
+ *this_pos = *other;
+ ++this_pos;
+ ++other_pos;
+ }
+ _end = this_pos;
+ return *this;
+ }
+
+private:
+ T * _end;
+ T _data[max_size];
+};
+
+} // namespace olm
+
+#endif /* OLM_LIST_HH_ */
--- /dev/null
+/* Copyright 2016 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OLM_MEGOLM_H_
+#define OLM_MEGOLM_H_
+
+/**
+ * implementation of the Megolm multi-part ratchet used in group chats.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+
+// Note: exports in this file are only for unit tests. Nobody else should be
+// using this externally
+#include "olm/olm_export.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * number of bytes in each part of the ratchet; this should be the same as
+ * the length of the hash function used in the HMAC (32 bytes for us, as we
+ * use HMAC-SHA-256)
+ */
+#define MEGOLM_RATCHET_PART_LENGTH 32 /* SHA256_OUTPUT_LENGTH */
+
+/**
+ * number of parts in the ratchet; the advance() implementations rely on
+ * this being 4.
+ */
+#define MEGOLM_RATCHET_PARTS 4
+
+#define MEGOLM_RATCHET_LENGTH (MEGOLM_RATCHET_PARTS * MEGOLM_RATCHET_PART_LENGTH)
+
+typedef struct Megolm {
+ uint8_t data[MEGOLM_RATCHET_PARTS][MEGOLM_RATCHET_PART_LENGTH];
+ uint32_t counter;
+} Megolm;
+
+
+/**
+ * The cipher used in megolm-backed conversations
+ *
+ * (AES256 + SHA256, with keys based on an HKDF with info of MEGOLM_KEYS)
+ */
+extern const struct _olm_cipher *megolm_cipher;
+
+/**
+ * initialize the megolm ratchet. random_data should be at least
+ * MEGOLM_RATCHET_LENGTH bytes of randomness.
+ */
+OLM_EXPORT void megolm_init(Megolm *megolm, uint8_t const *random_data, uint32_t counter);
+
+/** Returns the number of bytes needed to store a megolm */
+OLM_EXPORT size_t megolm_pickle_length(const Megolm *megolm);
+
+/**
+ * Pickle the megolm. Returns a pointer to the next free space in the buffer.
+ */
+OLM_EXPORT uint8_t * megolm_pickle(const Megolm *megolm, uint8_t *pos);
+
+/**
+ * Unpickle the megolm. Returns a pointer to the next item in the buffer.
+ */
+OLM_EXPORT const uint8_t * megolm_unpickle(Megolm *megolm, const uint8_t *pos,
+ const uint8_t *end);
+
+
+/** advance the ratchet by one step */
+OLM_EXPORT void megolm_advance(Megolm *megolm);
+
+/**
+ * get the key data in the ratchet. The returned data is
+ * MEGOLM_RATCHET_LENGTH bytes long.
+ */
+#define megolm_get_data(megolm) ((const uint8_t *)((megolm)->data))
+
+/** advance the ratchet to a given count */
+OLM_EXPORT void megolm_advance_to(Megolm *megolm, uint32_t advance_to);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* OLM_MEGOLM_H_ */
--- /dev/null
+/* Copyright 2015, 2016 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* C bindings for memory functions */
+
+
+#ifndef OLM_MEMORY_H_
+#define OLM_MEMORY_H_
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Clear the memory held in the buffer. This is more resilient to being
+ * optimised away than memset or bzero.
+ */
+void _olm_unset(
+ void volatile * buffer, size_t buffer_length
+);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+
+#endif /* OLM_MEMORY_H_ */
--- /dev/null
+/* Copyright 2015, 2016 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+#include <type_traits>
+
+namespace olm {
+
+/** Clear the memory held in the buffer */
+void unset(
+ void volatile * buffer, std::size_t buffer_length
+);
+
+/** Clear the memory backing an object */
+template<typename T>
+void unset(T & value) {
+ unset(reinterpret_cast<void volatile *>(&value), sizeof(T));
+}
+
+/** Check if two buffers are equal in constant time. */
+bool is_equal(
+ std::uint8_t const * buffer_a,
+ std::uint8_t const * buffer_b,
+ std::size_t length
+);
+
+/** Check if two fixed size arrays are equals */
+template<typename T>
+bool array_equal(
+ T const & array_a,
+ T const & array_b
+) {
+ static_assert(
+ std::is_array<T>::value
+ && std::is_convertible<T, std::uint8_t *>::value
+ && sizeof(T) > 0,
+ "Arguments to array_equal must be std::uint8_t arrays[]."
+ );
+ return is_equal(array_a, array_b, sizeof(T));
+}
+
+/** Copy into a fixed size array */
+template<typename T>
+std::uint8_t const * load_array(
+ T & destination,
+ std::uint8_t const * source
+) {
+ static_assert(
+ std::is_array<T>::value
+ && std::is_convertible<T, std::uint8_t *>::value
+ && sizeof(T) > 0,
+ "The first argument to load_array must be a std::uint8_t array[]."
+ );
+ std::memcpy(destination, source, sizeof(T));
+ return source + sizeof(T);
+}
+
+/** Copy from a fixed size array */
+template<typename T>
+std::uint8_t * store_array(
+ std::uint8_t * destination,
+ T const & source
+) {
+ static_assert(
+ std::is_array<T>::value
+ && std::is_convertible<T, std::uint8_t *>::value
+ && sizeof(T) > 0,
+ "The second argument to store_array must be a std::uint8_t array[]."
+ );
+ std::memcpy(destination, source, sizeof(T));
+ return destination + sizeof(T);
+}
+
+} // namespace olm
--- /dev/null
+/* Copyright 2016 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * functions for encoding and decoding messages in the Olm protocol.
+ *
+ * Some of these functions have only C++ bindings, and are declared in
+ * message.hh; in time, they should probably be converted to plain C and
+ * declared here.
+ */
+
+#ifndef OLM_MESSAGE_H_
+#define OLM_MESSAGE_H_
+
+#include <stdint.h>
+#include <stddef.h>
+
+// Note: exports in this file are only for unit tests. Nobody else should be
+// using this externally
+#include "olm/olm_export.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * The length of the buffer needed to hold a group message.
+ */
+OLM_EXPORT size_t _olm_encode_group_message_length(
+ uint32_t chain_index,
+ size_t ciphertext_length,
+ size_t mac_length,
+ size_t signature_length
+);
+
+/**
+ * Writes the message headers into the output buffer.
+ *
+ * version: version number of the olm protocol
+ * message_index: message index
+ * ciphertext_length: length of the ciphertext
+ * output: where to write the output. Should be at least
+ * olm_encode_group_message_length() bytes long.
+ * ciphertext_ptr: returns the address that the ciphertext
+ * should be written to, followed by the MAC and the
+ * signature.
+ *
+ * Returns the size of the message, up to the MAC.
+ */
+OLM_EXPORT size_t _olm_encode_group_message(
+ uint8_t version,
+ uint32_t message_index,
+ size_t ciphertext_length,
+ uint8_t *output,
+ uint8_t **ciphertext_ptr
+);
+
+
+struct _OlmDecodeGroupMessageResults {
+ uint8_t version;
+ uint32_t message_index;
+ int has_message_index;
+ const uint8_t *ciphertext;
+ size_t ciphertext_length;
+};
+
+
+/**
+ * Reads the message headers from the input buffer.
+ */
+OLM_EXPORT void _olm_decode_group_message(
+ const uint8_t *input, size_t input_length,
+ size_t mac_length, size_t signature_length,
+
+ /* output structure: updated with results */
+ struct _OlmDecodeGroupMessageResults *results
+);
+
+
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* OLM_MESSAGE_H_ */
--- /dev/null
+/* Copyright 2015 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/**
+ * functions for encoding and decoding messages in the Olm protocol.
+ *
+ * Some of these functions have plain-C bindings, and are declared in
+ * message.h; in time, all of the functions declared here should probably be
+ * converted to plain C and moved to message.h.
+ */
+
+#include "message.h"
+
+#include <cstddef>
+#include <cstdint>
+
+// Note: exports in this file are only for unit tests. Nobody else should be
+// using this externally
+#include "olm/olm_export.h"
+
+namespace olm {
+
+/**
+ * The length of the buffer needed to hold a message.
+ */
+OLM_EXPORT std::size_t encode_message_length(
+ std::uint32_t counter,
+ std::size_t ratchet_key_length,
+ std::size_t ciphertext_length,
+ std::size_t mac_length
+);
+
+
+struct MessageWriter {
+ std::uint8_t * ratchet_key;
+ std::uint8_t * ciphertext;
+};
+
+
+struct MessageReader {
+ std::uint8_t version;
+ bool has_counter;
+ std::uint32_t counter;
+ std::uint8_t const * input; std::size_t input_length;
+ std::uint8_t const * ratchet_key; std::size_t ratchet_key_length;
+ std::uint8_t const * ciphertext; std::size_t ciphertext_length;
+};
+
+
+/**
+ * Writes the message headers into the output buffer.
+ * Populates the writer struct with pointers into the output buffer.
+ */
+OLM_EXPORT void encode_message(
+ MessageWriter & writer,
+ std::uint8_t version,
+ std::uint32_t counter,
+ std::size_t ratchet_key_length,
+ std::size_t ciphertext_length,
+ std::uint8_t * output
+);
+
+
+/**
+ * Reads the message headers from the input buffer.
+ * Populates the reader struct with pointers into the input buffer.
+ */
+OLM_EXPORT void decode_message(
+ MessageReader & reader,
+ std::uint8_t const * input, std::size_t input_length,
+ std::size_t mac_length
+);
+
+
+struct PreKeyMessageWriter {
+ std::uint8_t * identity_key;
+ std::uint8_t * base_key;
+ std::uint8_t * one_time_key;
+ std::uint8_t * message;
+};
+
+
+struct PreKeyMessageReader {
+ std::uint8_t version;
+ std::uint8_t const * identity_key; std::size_t identity_key_length;
+ std::uint8_t const * base_key; std::size_t base_key_length;
+ std::uint8_t const * one_time_key; std::size_t one_time_key_length;
+ std::uint8_t const * message; std::size_t message_length;
+};
+
+
+/**
+ * The length of the buffer needed to hold a message.
+ */
+std::size_t encode_one_time_key_message_length(
+ std::size_t identity_key_length,
+ std::size_t base_key_length,
+ std::size_t one_time_key_length,
+ std::size_t message_length
+);
+
+
+/**
+ * Writes the message headers into the output buffer.
+ * Populates the writer struct with pointers into the output buffer.
+ */
+void encode_one_time_key_message(
+ PreKeyMessageWriter & writer,
+ std::uint8_t version,
+ std::size_t identity_key_length,
+ std::size_t base_key_length,
+ std::size_t one_time_key_length,
+ std::size_t message_length,
+ std::uint8_t * output
+);
+
+
+/**
+ * Reads the message headers from the input buffer.
+ * Populates the reader struct with pointers into the input buffer.
+ */
+void decode_one_time_key_message(
+ PreKeyMessageReader & reader,
+ std::uint8_t const * input, std::size_t input_length
+);
+
+
+} // namespace olm
--- /dev/null
+/* Copyright 2015, 2016 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OLM_H_
+#define OLM_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "olm/error.h"
+#include "olm/inbound_group_session.h"
+#include "olm/outbound_group_session.h"
+
+#include "olm/olm_export.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static const size_t OLM_MESSAGE_TYPE_PRE_KEY = 0;
+static const size_t OLM_MESSAGE_TYPE_MESSAGE = 1;
+
+typedef struct OlmAccount OlmAccount;
+typedef struct OlmSession OlmSession;
+typedef struct OlmUtility OlmUtility;
+
+/** Get the version number of the library.
+ * Arguments will be updated if non-null.
+ */
+OLM_EXPORT void olm_get_library_version(uint8_t *major, uint8_t *minor, uint8_t *patch);
+
+/** The size of an account object in bytes */
+OLM_EXPORT size_t olm_account_size(void);
+
+/** The size of a session object in bytes */
+OLM_EXPORT size_t olm_session_size(void);
+
+/** The size of a utility object in bytes */
+OLM_EXPORT size_t olm_utility_size(void);
+
+/** Initialise an account object using the supplied memory
+ * The supplied memory must be at least olm_account_size() bytes */
+OLM_EXPORT OlmAccount * olm_account(
+ void * memory
+);
+
+/** Initialise a session object using the supplied memory
+ * The supplied memory must be at least olm_session_size() bytes */
+OLM_EXPORT OlmSession * olm_session(
+ void * memory
+);
+
+/** Initialise a utility object using the supplied memory
+ * The supplied memory must be at least olm_utility_size() bytes */
+OLM_EXPORT OlmUtility * olm_utility(
+ void * memory
+);
+
+/** The value that olm will return from a function if there was an error */
+OLM_EXPORT size_t olm_error(void);
+
+/** A null terminated string describing the most recent error to happen to an
+ * account */
+OLM_EXPORT const char * olm_account_last_error(
+ OlmAccount const * account
+);
+
+/** An error code describing the most recent error to happen to an account */
+OLM_EXPORT enum OlmErrorCode olm_account_last_error_code(
+ OlmAccount const * account
+);
+
+/** A null terminated string describing the most recent error to happen to a
+ * session */
+OLM_EXPORT const char * olm_session_last_error(
+ OlmSession const * session
+);
+
+/** An error code describing the most recent error to happen to a session */
+OLM_EXPORT enum OlmErrorCode olm_session_last_error_code(
+ OlmSession const * session
+);
+
+/** A null terminated string describing the most recent error to happen to a
+ * utility */
+OLM_EXPORT const char * olm_utility_last_error(
+ OlmUtility const * utility
+);
+
+/** An error code describing the most recent error to happen to a utility */
+OLM_EXPORT enum OlmErrorCode olm_utility_last_error_code(
+ OlmUtility const * utility
+);
+
+/** Clears the memory used to back this account */
+OLM_EXPORT size_t olm_clear_account(
+ OlmAccount * account
+);
+
+/** Clears the memory used to back this session */
+OLM_EXPORT size_t olm_clear_session(
+ OlmSession * session
+);
+
+/** Clears the memory used to back this utility */
+OLM_EXPORT size_t olm_clear_utility(
+ OlmUtility * utility
+);
+
+/** Returns the number of bytes needed to store an account */
+OLM_EXPORT size_t olm_pickle_account_length(
+ OlmAccount const * account
+);
+
+/** Returns the number of bytes needed to store a session */
+OLM_EXPORT size_t olm_pickle_session_length(
+ OlmSession const * session
+);
+
+/** Stores an account as a base64 string. Encrypts the account using the
+ * supplied key. Returns the length of the pickled account on success.
+ * Returns olm_error() on failure. If the pickle output buffer
+ * is smaller than olm_pickle_account_length() then
+ * olm_account_last_error() will be "OUTPUT_BUFFER_TOO_SMALL" */
+OLM_EXPORT size_t olm_pickle_account(
+ OlmAccount * account,
+ void const * key, size_t key_length,
+ void * pickled, size_t pickled_length
+);
+
+/** Stores a session as a base64 string. Encrypts the session using the
+ * supplied key. Returns the length of the pickled session on success.
+ * Returns olm_error() on failure. If the pickle output buffer
+ * is smaller than olm_pickle_session_length() then
+ * olm_session_last_error() will be "OUTPUT_BUFFER_TOO_SMALL" */
+OLM_EXPORT size_t olm_pickle_session(
+ OlmSession * session,
+ void const * key, size_t key_length,
+ void * pickled, size_t pickled_length
+);
+
+/** Loads an account from a pickled base64 string. Decrypts the account using
+ * the supplied key. Returns olm_error() on failure. If the key doesn't
+ * match the one used to encrypt the account then olm_account_last_error()
+ * will be "BAD_ACCOUNT_KEY". If the base64 couldn't be decoded then
+ * olm_account_last_error() will be "INVALID_BASE64". The input pickled
+ * buffer is destroyed */
+OLM_EXPORT size_t olm_unpickle_account(
+ OlmAccount * account,
+ void const * key, size_t key_length,
+ void * pickled, size_t pickled_length
+);
+
+/** Loads a session from a pickled base64 string. Decrypts the session using
+ * the supplied key. Returns olm_error() on failure. If the key doesn't
+ * match the one used to encrypt the account then olm_session_last_error()
+ * will be "BAD_ACCOUNT_KEY". If the base64 couldn't be decoded then
+ * olm_session_last_error() will be "INVALID_BASE64". The input pickled
+ * buffer is destroyed */
+OLM_EXPORT size_t olm_unpickle_session(
+ OlmSession * session,
+ void const * key, size_t key_length,
+ void * pickled, size_t pickled_length
+);
+
+/** The number of random bytes needed to create an account.*/
+OLM_EXPORT size_t olm_create_account_random_length(
+ OlmAccount const * account
+);
+
+/** Creates a new account. Returns olm_error() on failure. If there weren't
+ * enough random bytes then olm_account_last_error() will be
+ * "NOT_ENOUGH_RANDOM" */
+OLM_EXPORT size_t olm_create_account(
+ OlmAccount * account,
+ void * random, size_t random_length
+);
+
+/** The size of the output buffer needed to hold the identity keys */
+OLM_EXPORT size_t olm_account_identity_keys_length(
+ OlmAccount const * account
+);
+
+/** Writes the public parts of the identity keys for the account into the
+ * identity_keys output buffer. Returns olm_error() on failure. If the
+ * identity_keys buffer was too small then olm_account_last_error() will be
+ * "OUTPUT_BUFFER_TOO_SMALL". */
+OLM_EXPORT size_t olm_account_identity_keys(
+ OlmAccount * account,
+ void * identity_keys, size_t identity_key_length
+);
+
+
+/** The length of an ed25519 signature encoded as base64. */
+OLM_EXPORT size_t olm_account_signature_length(
+ OlmAccount const * account
+);
+
+/** Signs a message with the ed25519 key for this account. Returns olm_error()
+ * on failure. If the signature buffer was too small then
+ * olm_account_last_error() will be "OUTPUT_BUFFER_TOO_SMALL" */
+OLM_EXPORT size_t olm_account_sign(
+ OlmAccount * account,
+ void const * message, size_t message_length,
+ void * signature, size_t signature_length
+);
+
+/** The size of the output buffer needed to hold the one time keys */
+OLM_EXPORT size_t olm_account_one_time_keys_length(
+ OlmAccount const * account
+);
+
+/** Writes the public parts of the unpublished one time keys for the account
+ * into the one_time_keys output buffer.
+ * <p>
+ * The returned data is a JSON-formatted object with the single property
+ * <tt>curve25519</tt>, which is itself an object mapping key id to
+ * base64-encoded Curve25519 key. For example:
+ * <pre>
+ * {
+ * curve25519: {
+ * "AAAAAA": "wo76WcYtb0Vk/pBOdmduiGJ0wIEjW4IBMbbQn7aSnTo",
+ * "AAAAAB": "LRvjo46L1X2vx69sS9QNFD29HWulxrmW11Up5AfAjgU"
+ * }
+ * }
+ * </pre>
+ * Returns olm_error() on failure.
+ * <p>
+ * If the one_time_keys buffer was too small then olm_account_last_error()
+ * will be "OUTPUT_BUFFER_TOO_SMALL". */
+OLM_EXPORT size_t olm_account_one_time_keys(
+ OlmAccount * account,
+ void * one_time_keys, size_t one_time_keys_length
+);
+
+/** Marks the current set of one time keys and fallback key as being published
+ * Once marked as published, the one time keys will no longer be returned by
+ * olm_account_one_time_keys(), and the fallback key will no longer be returned
+ * by olm_account_unpublished_fallback_key().
+ *
+ * Returns the number of one-time keys that were marked as published. Note that
+ * this count does not include the fallback key. */
+OLM_EXPORT size_t olm_account_mark_keys_as_published(
+ OlmAccount * account
+);
+
+/** The largest number of one time keys this account can store. */
+OLM_EXPORT size_t olm_account_max_number_of_one_time_keys(
+ OlmAccount const * account
+);
+
+/** The number of random bytes needed to generate a given number of new one
+ * time keys. */
+OLM_EXPORT size_t olm_account_generate_one_time_keys_random_length(
+ OlmAccount const * account,
+ size_t number_of_keys
+);
+
+/** Generates a number of new one time keys. If the total number of keys stored
+ * by this account exceeds max_number_of_one_time_keys() then the old keys are
+ * discarded. Returns olm_error() on error. If the number of random bytes is
+ * too small then olm_account_last_error() will be "NOT_ENOUGH_RANDOM". */
+OLM_EXPORT size_t olm_account_generate_one_time_keys(
+ OlmAccount * account,
+ size_t number_of_keys,
+ void * random, size_t random_length
+);
+
+/** The number of random bytes needed to generate a fallback key. */
+OLM_EXPORT size_t olm_account_generate_fallback_key_random_length(
+ OlmAccount const * account
+);
+
+/** Generates a new fallback key. Only one previous fallback key is
+ * stored. Returns olm_error() on error. If the number of random bytes is too
+ * small then olm_account_last_error() will be "NOT_ENOUGH_RANDOM". */
+OLM_EXPORT size_t olm_account_generate_fallback_key(
+ OlmAccount * account,
+ void * random, size_t random_length
+);
+
+/** The number of bytes needed to hold the fallback key as returned by
+ * olm_account_fallback_key. */
+OLM_EXPORT size_t olm_account_fallback_key_length(
+ OlmAccount const * account
+);
+
+/** Deprecated: use olm_account_unpublished_fallback_key instead */
+OLM_EXPORT size_t olm_account_fallback_key(
+ OlmAccount * account,
+ void * fallback_key, size_t fallback_key_size
+);
+
+/** The number of bytes needed to hold the unpublished fallback key as returned
+ * by olm_account_unpublished fallback_key. */
+OLM_EXPORT size_t olm_account_unpublished_fallback_key_length(
+ OlmAccount const * account
+);
+
+/** Returns the fallback key (if present, and if unpublished) into the
+ * fallback_key buffer */
+OLM_EXPORT size_t olm_account_unpublished_fallback_key(
+ OlmAccount * account,
+ void * fallback_key, size_t fallback_key_size
+);
+
+/** Forget about the old fallback key. This should be called once you are
+ * reasonably certain that you will not receive any more messages that use
+ * the old fallback key (e.g. 5 minutes after the new fallback key has been
+ * published).
+ */
+OLM_EXPORT void olm_account_forget_old_fallback_key(
+ OlmAccount * account
+);
+
+
+/** The number of random bytes needed to create an outbound session */
+OLM_EXPORT size_t olm_create_outbound_session_random_length(
+ OlmSession const * session
+);
+
+/** Creates a new out-bound session for sending messages to a given identity_key
+ * and one_time_key. Returns olm_error() on failure. If the keys couldn't be
+ * decoded as base64 then olm_session_last_error() will be "INVALID_BASE64"
+ * If there weren't enough random bytes then olm_session_last_error() will
+ * be "NOT_ENOUGH_RANDOM". */
+OLM_EXPORT size_t olm_create_outbound_session(
+ OlmSession * session,
+ OlmAccount const * account,
+ void const * their_identity_key, size_t their_identity_key_length,
+ void const * their_one_time_key, size_t their_one_time_key_length,
+ void * random, size_t random_length
+);
+
+/** Create a new in-bound session for sending/receiving messages from an
+ * incoming PRE_KEY message. Returns olm_error() on failure. If the base64
+ * couldn't be decoded then olm_session_last_error will be "INVALID_BASE64".
+ * If the message was for an unsupported protocol version then
+ * olm_session_last_error() will be "BAD_MESSAGE_VERSION". If the message
+ * couldn't be decoded then olm_session_last_error() will be
+ * "BAD_MESSAGE_FORMAT". If the message refers to an unknown one time
+ * key then olm_session_last_error() will be "BAD_MESSAGE_KEY_ID". */
+OLM_EXPORT size_t olm_create_inbound_session(
+ OlmSession * session,
+ OlmAccount * account,
+ void * one_time_key_message, size_t message_length
+);
+
+/** Same as olm_create_inbound_session, but ensures that the identity key
+ * in the pre-key message matches the expected identity key, supplied via the
+ * `their_identity_key` parameter. Fails early if there is no match. */
+OLM_EXPORT size_t olm_create_inbound_session_from(
+ OlmSession * session,
+ OlmAccount * account,
+ void const * their_identity_key, size_t their_identity_key_length,
+ void * one_time_key_message, size_t message_length
+);
+
+/** The length of the buffer needed to return the id for this session. */
+OLM_EXPORT size_t olm_session_id_length(
+ OlmSession const * session
+);
+
+/** An identifier for this session. Will be the same for both ends of the
+ * conversation. If the id buffer is too small then olm_session_last_error()
+ * will be "OUTPUT_BUFFER_TOO_SMALL". */
+OLM_EXPORT size_t olm_session_id(
+ OlmSession * session,
+ void * id, size_t id_length
+);
+
+OLM_EXPORT int olm_session_has_received_message(
+ OlmSession const *session
+);
+
+/**
+ * Write a null-terminated string describing the internal state of an olm
+ * session to the buffer provided for debugging and logging purposes. If the
+ * buffer is not large enough to hold the entire string, it will be truncated
+ * and will end with "...". A buffer length of 600 will be enough to hold any
+ * output.
+ */
+OLM_EXPORT void olm_session_describe(OlmSession * session, char *buf, size_t buflen);
+
+/** Checks if the PRE_KEY message is for this in-bound session. This can happen
+ * if multiple messages are sent to this account before this account sends a
+ * message in reply. The one_time_key_message buffer is destroyed. Returns 1 if
+ * the session matches. Returns 0 if the session does not match. Returns
+ * olm_error() on failure. If the base64 couldn't be decoded then
+ * olm_session_last_error will be "INVALID_BASE64". If the message was for an
+ * unsupported protocol version then olm_session_last_error() will be
+ * "BAD_MESSAGE_VERSION". If the message couldn't be decoded then then
+ * olm_session_last_error() will be "BAD_MESSAGE_FORMAT". */
+OLM_EXPORT size_t olm_matches_inbound_session(
+ OlmSession * session,
+ void * one_time_key_message, size_t message_length
+);
+
+/** Checks if the PRE_KEY message is for this in-bound session. This can happen
+ * if multiple messages are sent to this account before this account sends a
+ * message in reply. The one_time_key_message buffer is destroyed. Returns 1 if
+ * the session matches. Returns 0 if the session does not match. Returns
+ * olm_error() on failure. If the base64 couldn't be decoded then
+ * olm_session_last_error will be "INVALID_BASE64". If the message was for an
+ * unsupported protocol version then olm_session_last_error() will be
+ * "BAD_MESSAGE_VERSION". If the message couldn't be decoded then then
+ * olm_session_last_error() will be "BAD_MESSAGE_FORMAT". */
+OLM_EXPORT size_t olm_matches_inbound_session_from(
+ OlmSession * session,
+ void const * their_identity_key, size_t their_identity_key_length,
+ void * one_time_key_message, size_t message_length
+);
+
+/** Removes the one time keys that the session used from the account. Returns
+ * olm_error() on failure. If the account doesn't have any matching one time
+ * keys then olm_account_last_error() will be "BAD_MESSAGE_KEY_ID". */
+OLM_EXPORT size_t olm_remove_one_time_keys(
+ OlmAccount * account,
+ OlmSession * session
+);
+
+/** The type of the next message that olm_encrypt() will return. Returns
+ * OLM_MESSAGE_TYPE_PRE_KEY if the message will be a PRE_KEY message.
+ * Returns OLM_MESSAGE_TYPE_MESSAGE if the message will be a normal message.
+ * Returns olm_error on failure. */
+OLM_EXPORT size_t olm_encrypt_message_type(
+ OlmSession const * session
+);
+
+/** The number of random bytes needed to encrypt the next message. */
+OLM_EXPORT size_t olm_encrypt_random_length(
+ OlmSession const * session
+);
+
+/** The size of the next message in bytes for the given number of plain-text
+ * bytes. */
+OLM_EXPORT size_t olm_encrypt_message_length(
+ OlmSession const * session,
+ size_t plaintext_length
+);
+
+/** Encrypts a message using the session. Returns the length of the message in
+ * bytes on success. Writes the message as base64 into the message buffer.
+ * Returns olm_error() on failure. If the message buffer is too small then
+ * olm_session_last_error() will be "OUTPUT_BUFFER_TOO_SMALL". If there
+ * weren't enough random bytes then olm_session_last_error() will be
+ * "NOT_ENOUGH_RANDOM". */
+OLM_EXPORT size_t olm_encrypt(
+ OlmSession * session,
+ void const * plaintext, size_t plaintext_length,
+ void * random, size_t random_length,
+ void * message, size_t message_length
+);
+
+/** The maximum number of bytes of plain-text a given message could decode to.
+ * The actual size could be different due to padding. The input message buffer
+ * is destroyed. Returns olm_error() on failure. If the message base64
+ * couldn't be decoded then olm_session_last_error() will be
+ * "INVALID_BASE64". If the message is for an unsupported version of the
+ * protocol then olm_session_last_error() will be "BAD_MESSAGE_VERSION".
+ * If the message couldn't be decoded then olm_session_last_error() will be
+ * "BAD_MESSAGE_FORMAT". */
+OLM_EXPORT size_t olm_decrypt_max_plaintext_length(
+ OlmSession * session,
+ size_t message_type,
+ void * message, size_t message_length
+);
+
+/** Decrypts a message using the session. The input message buffer is destroyed.
+ * Returns the length of the plain-text on success. Returns olm_error() on
+ * failure. If the plain-text buffer is smaller than
+ * olm_decrypt_max_plaintext_length() then olm_session_last_error()
+ * will be "OUTPUT_BUFFER_TOO_SMALL". If the base64 couldn't be decoded then
+ * olm_session_last_error() will be "INVALID_BASE64". If the message is for
+ * an unsupported version of the protocol then olm_session_last_error() will
+ * be "BAD_MESSAGE_VERSION". If the message couldn't be decoded then
+ * olm_session_last_error() will be BAD_MESSAGE_FORMAT".
+ * If the MAC on the message was invalid then olm_session_last_error() will
+ * be "BAD_MESSAGE_MAC". */
+OLM_EXPORT size_t olm_decrypt(
+ OlmSession * session,
+ size_t message_type,
+ void * message, size_t message_length,
+ void * plaintext, size_t max_plaintext_length
+);
+
+/** The length of the buffer needed to hold the SHA-256 hash. */
+OLM_EXPORT size_t olm_sha256_length(
+ OlmUtility const * utility
+);
+
+/** Calculates the SHA-256 hash of the input and encodes it as base64. If the
+ * output buffer is smaller than olm_sha256_length() then
+ * olm_utility_last_error() will be "OUTPUT_BUFFER_TOO_SMALL". */
+OLM_EXPORT size_t olm_sha256(
+ OlmUtility * utility,
+ void const * input, size_t input_length,
+ void * output, size_t output_length
+);
+
+/** Verify an ed25519 signature. If the key was too small then
+ * olm_utility_last_error() will be "INVALID_BASE64". If the signature was invalid
+ * then olm_utility_last_error() will be "BAD_MESSAGE_MAC". */
+OLM_EXPORT size_t olm_ed25519_verify(
+ OlmUtility * utility,
+ void const * key, size_t key_length,
+ void const * message, size_t message_length,
+ void * signature, size_t signature_length
+);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* OLM_H_ */
--- /dev/null
+/* this file exists only for compatibility with existing applications.
+ * You should use "#include <olm/olm.h>" instead.
+ */
+#include "olm/olm.h"
--- /dev/null
+
+#ifndef OLM_EXPORT_H
+#define OLM_EXPORT_H
+
+#ifdef OLM_STATIC_DEFINE
+# define OLM_EXPORT
+# define OLM_NO_EXPORT
+#else
+# ifndef OLM_EXPORT
+# ifdef olm_EXPORTS
+ /* We are building this library */
+# define OLM_EXPORT
+# else
+ /* We are using this library */
+# define OLM_EXPORT
+# endif
+# endif
+
+# ifndef OLM_NO_EXPORT
+# define OLM_NO_EXPORT
+# endif
+#endif
+
+#ifndef OLM_DEPRECATED
+# define OLM_DEPRECATED __attribute__ ((__deprecated__))
+#endif
+
+#ifndef OLM_DEPRECATED_EXPORT
+# define OLM_DEPRECATED_EXPORT OLM_EXPORT OLM_DEPRECATED
+#endif
+
+#ifndef OLM_DEPRECATED_NO_EXPORT
+# define OLM_DEPRECATED_NO_EXPORT OLM_NO_EXPORT OLM_DEPRECATED
+#endif
+
+#if 0 /* DEFINE_NO_DEPRECATED */
+# ifndef OLM_NO_DEPRECATED
+# define OLM_NO_DEPRECATED
+# endif
+#endif
+
+#endif /* OLM_EXPORT_H */
--- /dev/null
+/* Copyright 2016 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef OLM_OUTBOUND_GROUP_SESSION_H_
+#define OLM_OUTBOUND_GROUP_SESSION_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "olm/error.h"
+
+#include "olm/olm_export.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct OlmOutboundGroupSession OlmOutboundGroupSession;
+
+/** get the size of an outbound group session, in bytes. */
+OLM_EXPORT size_t olm_outbound_group_session_size(void);
+
+/**
+ * Initialise an outbound group session object using the supplied memory
+ * The supplied memory should be at least olm_outbound_group_session_size()
+ * bytes.
+ */
+OLM_EXPORT OlmOutboundGroupSession * olm_outbound_group_session(
+ void *memory
+);
+
+/**
+ * A null terminated string describing the most recent error to happen to a
+ * group session */
+OLM_EXPORT const char *olm_outbound_group_session_last_error(
+ const OlmOutboundGroupSession *session
+);
+
+/**
+ * An error code describing the most recent error to happen to a group
+ * session */
+OLM_EXPORT enum OlmErrorCode olm_outbound_group_session_last_error_code(
+ const OlmOutboundGroupSession *session
+);
+
+/** Clears the memory used to back this group session */
+OLM_EXPORT size_t olm_clear_outbound_group_session(
+ OlmOutboundGroupSession *session
+);
+
+/** Returns the number of bytes needed to store an outbound group session */
+OLM_EXPORT size_t olm_pickle_outbound_group_session_length(
+ const OlmOutboundGroupSession *session
+);
+
+/**
+ * Stores a group session as a base64 string. Encrypts the session using the
+ * supplied key. Returns the length of the session on success.
+ *
+ * Returns olm_error() on failure. If the pickle output buffer
+ * is smaller than olm_pickle_outbound_group_session_length() then
+ * olm_outbound_group_session_last_error() will be "OUTPUT_BUFFER_TOO_SMALL"
+ */
+OLM_EXPORT size_t olm_pickle_outbound_group_session(
+ OlmOutboundGroupSession *session,
+ void const * key, size_t key_length,
+ void * pickled, size_t pickled_length
+);
+
+/**
+ * Loads a group session from a pickled base64 string. Decrypts the session
+ * using the supplied key.
+ *
+ * Returns olm_error() on failure. If the key doesn't match the one used to
+ * encrypt the account then olm_outbound_group_session_last_error() will be
+ * "BAD_ACCOUNT_KEY". If the base64 couldn't be decoded then
+ * olm_outbound_group_session_last_error() will be "INVALID_BASE64". The input
+ * pickled buffer is destroyed
+ */
+OLM_EXPORT size_t olm_unpickle_outbound_group_session(
+ OlmOutboundGroupSession *session,
+ void const * key, size_t key_length,
+ void * pickled, size_t pickled_length
+);
+
+
+/** The number of random bytes needed to create an outbound group session */
+OLM_EXPORT size_t olm_init_outbound_group_session_random_length(
+ const OlmOutboundGroupSession *session
+);
+
+/**
+ * Start a new outbound group session. Returns olm_error() on failure. On
+ * failure last_error will be set with an error code. The last_error will be
+ * NOT_ENOUGH_RANDOM if the number of random bytes was too small.
+ */
+OLM_EXPORT size_t olm_init_outbound_group_session(
+ OlmOutboundGroupSession *session,
+ uint8_t *random, size_t random_length
+);
+
+/**
+ * The number of bytes that will be created by encrypting a message
+ */
+OLM_EXPORT size_t olm_group_encrypt_message_length(
+ OlmOutboundGroupSession *session,
+ size_t plaintext_length
+);
+
+/**
+ * Encrypt some plain-text. Returns the length of the encrypted message or
+ * olm_error() on failure. On failure last_error will be set with an
+ * error code. The last_error will be OUTPUT_BUFFER_TOO_SMALL if the output
+ * buffer is too small.
+ */
+OLM_EXPORT size_t olm_group_encrypt(
+ OlmOutboundGroupSession *session,
+ uint8_t const * plaintext, size_t plaintext_length,
+ uint8_t * message, size_t message_length
+);
+
+
+/**
+ * Get the number of bytes returned by olm_outbound_group_session_id()
+ */
+OLM_EXPORT size_t olm_outbound_group_session_id_length(
+ const OlmOutboundGroupSession *session
+);
+
+/**
+ * Get a base64-encoded identifier for this session.
+ *
+ * Returns the length of the session id on success or olm_error() on
+ * failure. On failure last_error will be set with an error code. The
+ * last_error will be OUTPUT_BUFFER_TOO_SMALL if the id buffer was too
+ * small.
+ */
+OLM_EXPORT size_t olm_outbound_group_session_id(
+ OlmOutboundGroupSession *session,
+ uint8_t * id, size_t id_length
+);
+
+/**
+ * Get the current message index for this session.
+ *
+ * Each message is sent with an increasing index; this returns the index for
+ * the next message.
+ */
+OLM_EXPORT uint32_t olm_outbound_group_session_message_index(
+ OlmOutboundGroupSession *session
+);
+
+/**
+ * Get the number of bytes returned by olm_outbound_group_session_key()
+ */
+OLM_EXPORT size_t olm_outbound_group_session_key_length(
+ const OlmOutboundGroupSession *session
+);
+
+/**
+ * Get the base64-encoded current ratchet key for this session.
+ *
+ * Each message is sent with a different ratchet key. This function returns the
+ * ratchet key that will be used for the next message.
+ *
+ * Returns the length of the ratchet key on success or olm_error() on
+ * failure. On failure last_error will be set with an error code. The
+ * last_error will be OUTPUT_BUFFER_TOO_SMALL if the buffer was too small.
+ */
+OLM_EXPORT size_t olm_outbound_group_session_key(
+ OlmOutboundGroupSession *session,
+ uint8_t * key, size_t key_length
+);
+
+
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* OLM_OUTBOUND_GROUP_SESSION_H_ */
--- /dev/null
+/* Copyright 2015-2016 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef OLM_PICKLE_H_
+#define OLM_PICKLE_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+/* Convenience macro for checking the return value of internal unpickling
+ * functions and returning early on failure. */
+#ifndef UNPICKLE_OK
+#define UNPICKLE_OK(x) do { if (!(x)) return NULL; } while(0)
+#endif
+
+/* Convenience macro for failing on corrupted pickles from public
+ * API unpickling functions. */
+#define FAIL_ON_CORRUPTED_PICKLE(pos, session) \
+ do { \
+ if (!pos) { \
+ session->last_error = OLM_CORRUPTED_PICKLE; \
+ return (size_t)-1; \
+ } \
+ } while(0)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _olm_ed25519_public_key;
+struct _olm_ed25519_key_pair;
+
+
+#define _olm_pickle_uint32_length(value) 4
+uint8_t * _olm_pickle_uint32(uint8_t * pos, uint32_t value);
+uint8_t const * _olm_unpickle_uint32(
+ uint8_t const * pos, uint8_t const * end,
+ uint32_t *value
+);
+
+
+#define _olm_pickle_bool_length(value) 1
+uint8_t * _olm_pickle_bool(uint8_t * pos, int value);
+uint8_t const * _olm_unpickle_bool(
+ uint8_t const * pos, uint8_t const * end,
+ int *value
+);
+
+#define _olm_pickle_bytes_length(bytes, bytes_length) (bytes_length)
+uint8_t * _olm_pickle_bytes(uint8_t * pos, uint8_t const * bytes,
+ size_t bytes_length);
+uint8_t const * _olm_unpickle_bytes(uint8_t const * pos, uint8_t const * end,
+ uint8_t * bytes, size_t bytes_length);
+
+
+/** Get the number of bytes needed to pickle an ed25519 public key */
+size_t _olm_pickle_ed25519_public_key_length(
+ const struct _olm_ed25519_public_key * value
+);
+
+/** Pickle the ed25519 public key. Returns a pointer to the next free space in
+ * the buffer. */
+uint8_t * _olm_pickle_ed25519_public_key(
+ uint8_t *pos, const struct _olm_ed25519_public_key * value
+);
+
+/** Unpickle the ed25519 public key. Returns a pointer to the next item in the
+ * buffer on success, NULL on error. */
+const uint8_t * _olm_unpickle_ed25519_public_key(
+ const uint8_t *pos, const uint8_t *end,
+ struct _olm_ed25519_public_key * value
+);
+
+/** Get the number of bytes needed to pickle an ed25519 key pair */
+size_t _olm_pickle_ed25519_key_pair_length(
+ const struct _olm_ed25519_key_pair * value
+);
+
+/** Pickle the ed25519 key pair. Returns a pointer to the next free space in
+ * the buffer. */
+uint8_t * _olm_pickle_ed25519_key_pair(
+ uint8_t *pos, const struct _olm_ed25519_key_pair * value
+);
+
+/** Unpickle the ed25519 key pair. Returns a pointer to the next item in the
+ * buffer on success, NULL on error. */
+const uint8_t * _olm_unpickle_ed25519_key_pair(
+ const uint8_t *pos, const uint8_t *end,
+ struct _olm_ed25519_key_pair * value
+);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* OLM_PICKLE_H */
--- /dev/null
+/* Copyright 2015 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef OLM_PICKLE_HH_
+#define OLM_PICKLE_HH_
+
+#include "olm/list.hh"
+#include "olm/crypto.h"
+
+#include <cstring>
+#include <cstdint>
+
+/* Convenience macro for checking the return value of internal unpickling
+ * functions and returning early on failure. */
+#ifndef UNPICKLE_OK
+#define UNPICKLE_OK(x) do { if (!(x)) return nullptr; } while(0)
+#endif
+
+namespace olm {
+
+inline std::size_t pickle_length(
+ const std::uint32_t & value
+) {
+ return 4;
+}
+
+std::uint8_t * pickle(
+ std::uint8_t * pos,
+ std::uint32_t value
+);
+
+std::uint8_t const * unpickle(
+ std::uint8_t const * pos, std::uint8_t const * end,
+ std::uint32_t & value
+);
+
+
+inline std::size_t pickle_length(
+ const std::uint8_t & value
+) {
+ return 1;
+}
+
+std::uint8_t * pickle(
+ std::uint8_t * pos,
+ std::uint8_t value
+);
+
+std::uint8_t const * unpickle(
+ std::uint8_t const * pos, std::uint8_t const * end,
+ std::uint8_t & value
+);
+
+
+inline std::size_t pickle_length(
+ const bool & value
+) {
+ return 1;
+}
+
+std::uint8_t * pickle(
+ std::uint8_t * pos,
+ bool value
+);
+
+std::uint8_t const * unpickle(
+ std::uint8_t const * pos, std::uint8_t const * end,
+ bool & value
+);
+
+
+template<typename T, std::size_t max_size>
+std::size_t pickle_length(
+ olm::List<T, max_size> const & list
+) {
+ std::size_t length = pickle_length(std::uint32_t(list.size()));
+ for (auto const & value : list) {
+ length += pickle_length(value);
+ }
+ return length;
+}
+
+
+template<typename T, std::size_t max_size>
+std::uint8_t * pickle(
+ std::uint8_t * pos,
+ olm::List<T, max_size> const & list
+) {
+ pos = pickle(pos, std::uint32_t(list.size()));
+ for (auto const & value : list) {
+ pos = pickle(pos, value);
+ }
+ return pos;
+}
+
+
+template<typename T, std::size_t max_size>
+std::uint8_t const * unpickle(
+ std::uint8_t const * pos, std::uint8_t const * end,
+ olm::List<T, max_size> & list
+) {
+ std::uint32_t size;
+
+ pos = unpickle(pos, end, size);
+ if (!pos) {
+ return nullptr;
+ }
+
+ while (size-- && pos != end) {
+ T * value = list.insert(list.end());
+ pos = unpickle(pos, end, *value);
+
+ if (!pos) {
+ return nullptr;
+ }
+ }
+
+ return pos;
+}
+
+
+std::uint8_t * pickle_bytes(
+ std::uint8_t * pos,
+ std::uint8_t const * bytes, std::size_t bytes_length
+);
+
+std::uint8_t const * unpickle_bytes(
+ std::uint8_t const * pos, std::uint8_t const * end,
+ std::uint8_t * bytes, std::size_t bytes_length
+);
+
+
+std::size_t pickle_length(
+ const _olm_curve25519_public_key & value
+);
+
+
+std::uint8_t * pickle(
+ std::uint8_t * pos,
+ const _olm_curve25519_public_key & value
+);
+
+
+std::uint8_t const * unpickle(
+ std::uint8_t const * pos, std::uint8_t const * end,
+ _olm_curve25519_public_key & value
+);
+
+
+std::size_t pickle_length(
+ const _olm_curve25519_key_pair & value
+);
+
+
+std::uint8_t * pickle(
+ std::uint8_t * pos,
+ const _olm_curve25519_key_pair & value
+);
+
+
+std::uint8_t const * unpickle(
+ std::uint8_t const * pos, std::uint8_t const * end,
+ _olm_curve25519_key_pair & value
+);
+
+} // namespace olm
+
+
+
+
+#endif /* OLM_PICKLE_HH */
--- /dev/null
+/* Copyright 2016 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* functions for encrypting and decrypting pickled representations of objects */
+
+#ifndef OLM_PICKLE_ENCODING_H_
+#define OLM_PICKLE_ENCODING_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "olm/error.h"
+
+// Note: exports in this file are only for unit tests. Nobody else should be
+// using this externally
+#include "olm/olm_export.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * Get the number of bytes needed to encode a pickle of the length given
+ */
+OLM_EXPORT size_t _olm_enc_output_length(size_t raw_length);
+
+/**
+ * Get the point in the output buffer that the raw pickle should be written to.
+ *
+ * In order that we can use the same buffer for the raw pickle, and the encoded
+ * pickle, the raw pickle needs to be written at the end of the buffer. (The
+ * base-64 encoding would otherwise overwrite the end of the input before it
+ * was encoded.)
+ */
+OLM_EXPORT uint8_t *_olm_enc_output_pos(uint8_t * output, size_t raw_length);
+
+/**
+ * Encrypt and encode the given pickle in-situ.
+ *
+ * The raw pickle should have been written to enc_output_pos(pickle,
+ * raw_length).
+ *
+ * Returns the number of bytes in the encoded pickle.
+ */
+OLM_EXPORT size_t _olm_enc_output(
+ uint8_t const * key, size_t key_length,
+ uint8_t *pickle, size_t raw_length
+);
+
+/**
+ * Decode and decrypt the given pickle in-situ.
+ *
+ * Returns the number of bytes in the decoded pickle, or olm_error() on error,
+ * in which case *last_error will be updated, if last_error is non-NULL.
+ */
+OLM_EXPORT size_t _olm_enc_input(
+ uint8_t const * key, size_t key_length,
+ uint8_t * input, size_t b64_length,
+ enum OlmErrorCode * last_error
+);
+
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* OLM_PICKLE_ENCODING_H_ */
--- /dev/null
+/* Copyright 2018, 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OLM_PK_H_
+#define OLM_PK_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "olm/error.h"
+
+#include "olm/olm_export.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct OlmPkEncryption OlmPkEncryption;
+
+/* The size of an encryption object in bytes */
+OLM_EXPORT size_t olm_pk_encryption_size(void);
+
+/** Initialise an encryption object using the supplied memory
+ * The supplied memory must be at least olm_pk_encryption_size() bytes */
+OLM_EXPORT OlmPkEncryption *olm_pk_encryption(
+ void * memory
+);
+
+/** A null terminated string describing the most recent error to happen to an
+ * encryption object */
+OLM_EXPORT const char * olm_pk_encryption_last_error(
+ const OlmPkEncryption * encryption
+);
+
+/** An error code describing the most recent error to happen to an encryption
+ * object */
+OLM_EXPORT enum OlmErrorCode olm_pk_encryption_last_error_code(
+ const OlmPkEncryption * encryption
+);
+
+/** Clears the memory used to back this encryption object */
+OLM_EXPORT size_t olm_clear_pk_encryption(
+ OlmPkEncryption *encryption
+);
+
+/** Set the recipient's public key for encrypting to */
+OLM_EXPORT size_t olm_pk_encryption_set_recipient_key(
+ OlmPkEncryption *encryption,
+ void const *public_key, size_t public_key_length
+);
+
+/** Get the length of the ciphertext that will correspond to a plaintext of the
+ * given length. */
+OLM_EXPORT size_t olm_pk_ciphertext_length(
+ const OlmPkEncryption *encryption,
+ size_t plaintext_length
+);
+
+/** Get the length of the message authentication code. */
+OLM_EXPORT size_t olm_pk_mac_length(
+ const OlmPkEncryption *encryption
+);
+
+/** Get the length of a public or ephemeral key */
+OLM_EXPORT size_t olm_pk_key_length(void);
+
+/** The number of random bytes needed to encrypt a message. */
+OLM_EXPORT size_t olm_pk_encrypt_random_length(
+ const OlmPkEncryption *encryption
+);
+
+/** Encrypt a plaintext for the recipient set using
+ * olm_pk_encryption_set_recipient_key. Writes to the ciphertext, mac, and
+ * ephemeral_key buffers, whose values should be sent to the recipient. mac is
+ * a Message Authentication Code to ensure that the data is received and
+ * decrypted properly. ephemeral_key is the public part of the ephemeral key
+ * used (together with the recipient's key) to generate a symmetric encryption
+ * key. Returns olm_error() on failure. If the ciphertext, mac, or
+ * ephemeral_key buffers were too small then olm_pk_encryption_last_error()
+ * will be "OUTPUT_BUFFER_TOO_SMALL". If there weren't enough random bytes then
+ * olm_pk_encryption_last_error() will be "OLM_INPUT_BUFFER_TOO_SMALL". */
+OLM_EXPORT size_t olm_pk_encrypt(
+ OlmPkEncryption *encryption,
+ void const * plaintext, size_t plaintext_length,
+ void * ciphertext, size_t ciphertext_length,
+ void * mac, size_t mac_length,
+ void * ephemeral_key, size_t ephemeral_key_size,
+ const void * random, size_t random_length
+);
+
+typedef struct OlmPkDecryption OlmPkDecryption;
+
+/* The size of a decryption object in bytes */
+OLM_EXPORT size_t olm_pk_decryption_size(void);
+
+/** Initialise a decryption object using the supplied memory
+ * The supplied memory must be at least olm_pk_decryption_size() bytes */
+OLM_EXPORT OlmPkDecryption *olm_pk_decryption(
+ void * memory
+);
+
+/** A null terminated string describing the most recent error to happen to a
+ * decription object */
+OLM_EXPORT const char * olm_pk_decryption_last_error(
+ const OlmPkDecryption * decryption
+);
+
+/** An error code describing the most recent error to happen to a decription
+ * object */
+OLM_EXPORT enum OlmErrorCode olm_pk_decryption_last_error_code(
+ const OlmPkDecryption * decryption
+);
+
+/** Clears the memory used to back this decryption object */
+OLM_EXPORT size_t olm_clear_pk_decryption(
+ OlmPkDecryption *decryption
+);
+
+/** Get the number of bytes required to store an olm private key
+ */
+OLM_EXPORT size_t olm_pk_private_key_length(void);
+
+/** DEPRECATED: Use olm_pk_private_key_length()
+ */
+OLM_EXPORT size_t olm_pk_generate_key_random_length(void);
+
+/** Initialise the key from the private part of a key as returned by
+ * olm_pk_get_private_key(). The associated public key will be written to the
+ * pubkey buffer. Returns olm_error() on failure. If the pubkey buffer is too
+ * small then olm_pk_decryption_last_error() will be "OUTPUT_BUFFER_TOO_SMALL".
+ * If the private key was not long enough then olm_pk_decryption_last_error()
+ * will be "OLM_INPUT_BUFFER_TOO_SMALL".
+ *
+ * Note that the pubkey is a base64 encoded string, but the private key is
+ * an unencoded byte array
+ */
+OLM_EXPORT size_t olm_pk_key_from_private(
+ OlmPkDecryption * decryption,
+ void * pubkey, size_t pubkey_length,
+ const void * privkey, size_t privkey_length
+);
+
+/** DEPRECATED: Use olm_pk_key_from_private
+ */
+OLM_EXPORT size_t olm_pk_generate_key(
+ OlmPkDecryption * decryption,
+ void * pubkey, size_t pubkey_length,
+ const void * privkey, size_t privkey_length
+);
+
+/** Returns the number of bytes needed to store a decryption object. */
+OLM_EXPORT size_t olm_pickle_pk_decryption_length(
+ const OlmPkDecryption * decryption
+);
+
+/** Stores decryption object as a base64 string. Encrypts the object using the
+ * supplied key. Returns the length of the pickled object on success.
+ * Returns olm_error() on failure. If the pickle output buffer
+ * is smaller than olm_pickle_pk_decryption_length() then
+ * olm_pk_decryption_last_error() will be "OUTPUT_BUFFER_TOO_SMALL" */
+OLM_EXPORT size_t olm_pickle_pk_decryption(
+ OlmPkDecryption * decryption,
+ void const * key, size_t key_length,
+ void *pickled, size_t pickled_length
+);
+
+/** Loads a decryption object from a pickled base64 string. The associated
+ * public key will be written to the pubkey buffer. Decrypts the object using
+ * the supplied key. Returns olm_error() on failure. If the key doesn't
+ * match the one used to encrypt the account then olm_pk_decryption_last_error()
+ * will be "BAD_ACCOUNT_KEY". If the base64 couldn't be decoded then
+ * olm_pk_decryption_last_error() will be "INVALID_BASE64". The input pickled
+ * buffer is destroyed */
+OLM_EXPORT size_t olm_unpickle_pk_decryption(
+ OlmPkDecryption * decryption,
+ void const * key, size_t key_length,
+ void *pickled, size_t pickled_length,
+ void *pubkey, size_t pubkey_length
+);
+
+/** Get the length of the plaintext that will correspond to a ciphertext of the
+ * given length. */
+OLM_EXPORT size_t olm_pk_max_plaintext_length(
+ const OlmPkDecryption * decryption,
+ size_t ciphertext_length
+);
+
+/** Decrypt a ciphertext. The input ciphertext buffer is destroyed. See the
+ * olm_pk_encrypt function for descriptions of the ephemeral_key and mac
+ * arguments. Returns the length of the plaintext on success. Returns
+ * olm_error() on failure. If the plaintext buffer is too small then
+ * olm_pk_encryption_last_error() will be "OUTPUT_BUFFER_TOO_SMALL". */
+OLM_EXPORT size_t olm_pk_decrypt(
+ OlmPkDecryption * decryption,
+ void const * ephemeral_key, size_t ephemeral_key_length,
+ void const * mac, size_t mac_length,
+ void * ciphertext, size_t ciphertext_length,
+ void * plaintext, size_t max_plaintext_length
+);
+
+/**
+ * Get the private key for an OlmDecryption object as an unencoded byte array
+ * private_key must be a pointer to a buffer of at least
+ * olm_pk_private_key_length() bytes and this length must be passed in
+ * private_key_length. If the given buffer is too small, returns olm_error()
+ * and olm_pk_encryption_last_error() will be "OUTPUT_BUFFER_TOO_SMALL".
+ * Returns the number of bytes written.
+ */
+OLM_EXPORT size_t olm_pk_get_private_key(
+ OlmPkDecryption * decryption,
+ void *private_key, size_t private_key_length
+);
+
+typedef struct OlmPkSigning OlmPkSigning;
+
+/* The size of a signing object in bytes */
+OLM_EXPORT size_t olm_pk_signing_size(void);
+
+/** Initialise a signing object using the supplied memory
+ * The supplied memory must be at least olm_pk_signing_size() bytes */
+OLM_EXPORT OlmPkSigning *olm_pk_signing(
+ void * memory
+);
+
+/** A null terminated string describing the most recent error to happen to a
+ * signing object */
+OLM_EXPORT const char * olm_pk_signing_last_error(
+ const OlmPkSigning * sign
+);
+
+/** A null terminated string describing the most recent error to happen to a
+ * signing object */
+OLM_EXPORT enum OlmErrorCode olm_pk_signing_last_error_code(
+ const OlmPkSigning * sign
+);
+
+/** Clears the memory used to back this signing object */
+OLM_EXPORT size_t olm_clear_pk_signing(
+ OlmPkSigning *sign
+);
+
+/**
+ * Initialise the signing object with a public/private keypair from a seed. The
+ * associated public key will be written to the pubkey buffer. Returns
+ * olm_error() on failure. If the public key buffer is too small then
+ * olm_pk_signing_last_error() will be "OUTPUT_BUFFER_TOO_SMALL". If the seed
+ * buffer is too small then olm_pk_signing_last_error() will be
+ * "INPUT_BUFFER_TOO_SMALL".
+ */
+OLM_EXPORT size_t olm_pk_signing_key_from_seed(
+ OlmPkSigning * sign,
+ void * pubkey, size_t pubkey_length,
+ const void * seed, size_t seed_length
+);
+
+/**
+ * The size required for the seed for initialising a signing object.
+ */
+OLM_EXPORT size_t olm_pk_signing_seed_length(void);
+
+/**
+ * The size of the public key of a signing object.
+ */
+OLM_EXPORT size_t olm_pk_signing_public_key_length(void);
+
+/**
+ * The size of a signature created by a signing object.
+ */
+OLM_EXPORT size_t olm_pk_signature_length(void);
+
+/**
+ * Sign a message. The signature will be written to the signature
+ * buffer. Returns olm_error() on failure. If the signature buffer is too
+ * small, olm_pk_signing_last_error() will be "OUTPUT_BUFFER_TOO_SMALL".
+ */
+OLM_EXPORT size_t olm_pk_sign(
+ OlmPkSigning *sign,
+ uint8_t const * message, size_t message_length,
+ uint8_t * signature, size_t signature_length
+);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* OLM_PK_H_ */
--- /dev/null
+/* Copyright 2015 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cstdint>
+
+#include "olm/crypto.h"
+#include "olm/list.hh"
+#include "olm/error.h"
+
+// Note: exports in this file are only for unit tests. Nobody else should be
+// using this externally
+#include "olm/olm_export.h"
+
+struct _olm_cipher;
+
+namespace olm {
+
+/** length of a shared key: the root key R(i), chain key C(i,j), and message key
+ * M(i,j)). They are all only used to stuff into HMACs, so could be any length
+ * for that. The chain key and message key are both derived from SHA256
+ * operations, so their length is determined by that. */
+const std::size_t OLM_SHARED_KEY_LENGTH = SHA256_OUTPUT_LENGTH;
+
+typedef std::uint8_t SharedKey[OLM_SHARED_KEY_LENGTH];
+
+struct ChainKey {
+ std::uint32_t index;
+ SharedKey key;
+};
+
+struct MessageKey {
+ std::uint32_t index;
+ SharedKey key;
+};
+
+
+struct SenderChain {
+ _olm_curve25519_key_pair ratchet_key;
+ ChainKey chain_key;
+};
+
+
+struct ReceiverChain {
+ _olm_curve25519_public_key ratchet_key;
+ ChainKey chain_key;
+};
+
+
+struct SkippedMessageKey {
+ _olm_curve25519_public_key ratchet_key;
+ MessageKey message_key;
+};
+
+
+static std::size_t const MAX_RECEIVER_CHAINS = 5;
+static std::size_t const MAX_SKIPPED_MESSAGE_KEYS = 40;
+
+
+struct KdfInfo {
+ std::uint8_t const * root_info;
+ std::size_t root_info_length;
+ std::uint8_t const * ratchet_info;
+ std::size_t ratchet_info_length;
+};
+
+
+struct OLM_EXPORT Ratchet {
+
+ Ratchet(
+ KdfInfo const & kdf_info,
+ _olm_cipher const *ratchet_cipher
+ );
+
+ /** A some strings identifying the application to feed into the KDF. */
+ KdfInfo const & kdf_info;
+
+ /** The AEAD cipher to use for encrypting messages. */
+ _olm_cipher const *ratchet_cipher;
+
+ /** The last error that happened encrypting or decrypting a message. */
+ OlmErrorCode last_error;
+
+ /** The root key is used to generate chain keys from the ephemeral keys.
+ * A new root_key derived each time a new chain is started. */
+ SharedKey root_key;
+
+ /** The sender chain is used to send messages. Each time a new ephemeral
+ * key is received from the remote server we generate a new sender chain
+ * with a new ephemeral key when we next send a message. */
+ List<SenderChain, 1> sender_chain;
+
+ /** The receiver chain is used to decrypt received messages. We store the
+ * last few chains so we can decrypt any out of order messages we haven't
+ * received yet. */
+ List<ReceiverChain, MAX_RECEIVER_CHAINS> receiver_chains;
+
+ /** List of message keys we've skipped over when advancing the receiver
+ * chain. */
+ List<SkippedMessageKey, MAX_SKIPPED_MESSAGE_KEYS> skipped_message_keys;
+
+ /** Initialise the session using a shared secret and the public part of the
+ * remote's first ratchet key */
+ void initialise_as_bob(
+ std::uint8_t const * shared_secret, std::size_t shared_secret_length,
+ _olm_curve25519_public_key const & their_ratchet_key
+ );
+
+ /** Initialise the session using a shared secret and the public/private key
+ * pair for the first ratchet key */
+ void initialise_as_alice(
+ std::uint8_t const * shared_secret, std::size_t shared_secret_length,
+ _olm_curve25519_key_pair const & our_ratchet_key
+ );
+
+ /** The number of bytes of output the encrypt method will write for
+ * a given message length. */
+ std::size_t encrypt_output_length(
+ std::size_t plaintext_length
+ ) const;
+
+ /** The number of bytes of random data the encrypt method will need to
+ * encrypt a message. This will be 32 bytes if the session needs to
+ * generate a new ephemeral key, or will be 0 bytes otherwise.*/
+ std::size_t encrypt_random_length() const;
+
+ /** Encrypt some plain-text. Returns the length of the encrypted message
+ * or std::size_t(-1) on failure. On failure last_error will be set with
+ * an error code. The last_error will be NOT_ENOUGH_RANDOM if the number
+ * of random bytes is too small. The last_error will be
+ * OUTPUT_BUFFER_TOO_SMALL if the output buffer is too small. */
+ std::size_t encrypt(
+ std::uint8_t const * plaintext, std::size_t plaintext_length,
+ std::uint8_t const * random, std::size_t random_length,
+ std::uint8_t * output, std::size_t max_output_length
+ );
+
+ /** An upper bound on the number of bytes of plain-text the decrypt method
+ * will write for a given input message length. */
+ std::size_t decrypt_max_plaintext_length(
+ std::uint8_t const * input, std::size_t input_length
+ );
+
+ /** Decrypt a message. Returns the length of the decrypted plain-text or
+ * std::size_t(-1) on failure. On failure last_error will be set with an
+ * error code. The last_error will be OUTPUT_BUFFER_TOO_SMALL if the
+ * plain-text buffer is too small. The last_error will be
+ * BAD_MESSAGE_VERSION if the message was encrypted with an unsupported
+ * version of the protocol. The last_error will be BAD_MESSAGE_FORMAT if
+ * the message headers could not be decoded. The last_error will be
+ * BAD_MESSAGE_MAC if the message could not be verified */
+ std::size_t decrypt(
+ std::uint8_t const * input, std::size_t input_length,
+ std::uint8_t * plaintext, std::size_t max_plaintext_length
+ );
+};
+
+
+std::size_t pickle_length(
+ Ratchet const & value
+);
+
+
+std::uint8_t * pickle(
+ std::uint8_t * pos,
+ Ratchet const & value
+);
+
+
+std::uint8_t const * unpickle(
+ std::uint8_t const * pos, std::uint8_t const * end,
+ Ratchet & value,
+ bool includes_chain_index
+);
+
+
+} // namespace olm
--- /dev/null
+/* Copyright 2018-2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef OLM_SAS_H_
+#define OLM_SAS_H_
+
+#include <stddef.h>
+
+#include "olm/error.h"
+
+#include "olm/olm_export.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @defgroup SAS Short Authentication String verification
+ * These functions are used for verifying keys using the Short Authentication
+ * String (SAS) method.
+ * @{
+ */
+
+typedef struct OlmSAS OlmSAS;
+
+/** A null terminated string describing the most recent error to happen to an
+ * SAS object. */
+OLM_EXPORT const char * olm_sas_last_error(
+ const OlmSAS * sas
+);
+
+/** An error code describing the most recent error to happen to an SAS
+ * object. */
+OLM_EXPORT enum OlmErrorCode olm_sas_last_error_code(
+ const OlmSAS * sas
+);
+
+/** The size of an SAS object in bytes. */
+OLM_EXPORT size_t olm_sas_size(void);
+
+/** Initialize an SAS object using the supplied memory.
+ * The supplied memory must be at least `olm_sas_size()` bytes. */
+OLM_EXPORT OlmSAS * olm_sas(
+ void * memory
+);
+
+/** Clears the memory used to back an SAS object. */
+OLM_EXPORT size_t olm_clear_sas(
+ OlmSAS * sas
+);
+
+/** The number of random bytes needed to create an SAS object. */
+OLM_EXPORT size_t olm_create_sas_random_length(
+ const OlmSAS * sas
+);
+
+/** Creates a new SAS object.
+ *
+ * @param[in] sas the SAS object to create, initialized by `olm_sas()`.
+ * @param[in] random array of random bytes. The contents of the buffer may be
+ * overwritten.
+ * @param[in] random_length the number of random bytes provided. Must be at
+ * least `olm_create_sas_random_length()`.
+ *
+ * @return `olm_error()` on failure. If there weren't enough random bytes then
+ * `olm_sas_last_error()` will be `NOT_ENOUGH_RANDOM`.
+ */
+OLM_EXPORT size_t olm_create_sas(
+ OlmSAS * sas,
+ void * random, size_t random_length
+);
+
+/** The size of a public key in bytes. */
+OLM_EXPORT size_t olm_sas_pubkey_length(const OlmSAS * sas);
+
+/** Get the public key for the SAS object.
+ *
+ * @param[in] sas the SAS object.
+ * @param[out] pubkey buffer to store the public key.
+ * @param[in] pubkey_length the size of the `pubkey` buffer. Must be at least
+ * `olm_sas_pubkey_length()`.
+ *
+ * @return `olm_error()` on failure. If the `pubkey` buffer is too small, then
+ * `olm_sas_last_error()` will be `OUTPUT_BUFFER_TOO_SMALL`.
+ */
+OLM_EXPORT size_t olm_sas_get_pubkey(
+ OlmSAS * sas,
+ void * pubkey, size_t pubkey_length
+);
+
+/** Sets the public key of other user.
+ *
+ * @param[in] sas the SAS object.
+ * @param[in] their_key the other user's public key. The contents of the
+ * buffer will be overwritten.
+ * @param[in] their_key_length the size of the `their_key` buffer.
+ *
+ * @return `olm_error()` on failure. If the `their_key` buffer is too small,
+ * then `olm_sas_last_error()` will be `INPUT_BUFFER_TOO_SMALL`.
+ */
+OLM_EXPORT size_t olm_sas_set_their_key(
+ OlmSAS *sas,
+ void * their_key, size_t their_key_length
+);
+
+/** Checks if their key was set.
+ *
+ * @param[in] sas the SAS object.
+ *
+ */
+OLM_EXPORT int olm_sas_is_their_key_set(
+ const OlmSAS *sas
+);
+
+/** Generate bytes to use for the short authentication string.
+ *
+ * @param[in] sas the SAS object.
+ * @param[in] info extra information to mix in when generating the bytes, as
+ * per the Matrix spec.
+ * @param[in] info_length the length of the `info` parameter.
+ * @param[out] output the output buffer.
+ * @param[in] output_length the size of the output buffer. For hex-based SAS
+ * as in the Matrix spec, this will be 5.
+ *
+ * @return `olm_error()` on failure. If their key wasn't set then
+ * `olm_sas_last_error()` will be `SAS_THEIR_KEY_NOT_SET`.
+ */
+OLM_EXPORT size_t olm_sas_generate_bytes(
+ OlmSAS * sas,
+ const void * info, size_t info_length,
+ void * output, size_t output_length
+);
+
+/** The size of the message authentication code generated by
+ * olm_sas_calculate_mac()`. */
+OLM_EXPORT size_t olm_sas_mac_length(
+ const OlmSAS *sas
+);
+
+/** Generate a message authentication code (MAC) based on the shared secret.
+ *
+ * @param[in] sas the SAS object.
+ * @param[in] input the message to produce the authentication code for.
+ * @param[in] input_length the length of the message.
+ * @param[in] info extra information to mix in when generating the MAC, as per
+ * the Matrix spec.
+ * @param[in] info_length the length of the `info` parameter.
+ * @param[out] mac the buffer in which to store the MAC.
+ * @param[in] mac_length the size of the `mac` buffer. Must be at least
+ * `olm_sas_mac_length()`
+ *
+ * @return `olm_error()` on failure. If the `mac` buffer is too small, then
+ * `olm_sas_last_error()` will be `OUTPUT_BUFFER_TOO_SMALL`.
+ */
+OLM_EXPORT size_t olm_sas_calculate_mac(
+ OlmSAS * sas,
+ const void * input, size_t input_length,
+ const void * info, size_t info_length,
+ void * mac, size_t mac_length
+);
+
+// A version of the calculate mac function that produces base64 strings that are
+// compatible with other base64 implementations.
+OLM_EXPORT size_t olm_sas_calculate_mac_fixed_base64(
+ OlmSAS * sas,
+ const void * input, size_t input_length,
+ const void * info, size_t info_length,
+ void * mac, size_t mac_length
+);
+
+// for compatibility with an old version of Riot
+OLM_EXPORT size_t olm_sas_calculate_mac_long_kdf(
+ OlmSAS * sas,
+ const void * input, size_t input_length,
+ const void * info, size_t info_length,
+ void * mac, size_t mac_length
+);
+
+/** @} */ // end of SAS group
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* OLM_SAS_H_ */
--- /dev/null
+/* Copyright 2015 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef OLM_SESSION_HH_
+#define OLM_SESSION_HH_
+
+#include "olm/ratchet.hh"
+
+// Note: exports in this file are only for unit tests. Nobody else should be
+// using this externally
+#include "olm/olm_export.h"
+
+namespace olm {
+
+struct Account;
+
+enum struct MessageType {
+ PRE_KEY = 0,
+ MESSAGE = 1,
+};
+
+struct OLM_EXPORT Session {
+
+ Session();
+
+ Ratchet ratchet;
+ OlmErrorCode last_error;
+
+ bool received_message;
+
+ _olm_curve25519_public_key alice_identity_key;
+ _olm_curve25519_public_key alice_base_key;
+ _olm_curve25519_public_key bob_one_time_key;
+
+ /** The number of random bytes that are needed to create a new outbound
+ * session. This will be 64 bytes since two ephemeral keys are needed. */
+ std::size_t new_outbound_session_random_length() const;
+
+ /** Start a new outbound session. Returns std::size_t(-1) on failure. On
+ * failure last_error will be set with an error code. The last_error will be
+ * NOT_ENOUGH_RANDOM if the number of random bytes was too small. */
+ std::size_t new_outbound_session(
+ Account const & local_account,
+ _olm_curve25519_public_key const & identity_key,
+ _olm_curve25519_public_key const & one_time_key,
+ std::uint8_t const * random, std::size_t random_length
+ );
+
+ /** Start a new inbound session from a pre-key message.
+ * Returns std::size_t(-1) on failure. On failure last_error will be set
+ * with an error code. The last_error will be BAD_MESSAGE_FORMAT if
+ * the message headers could not be decoded. */
+ std::size_t new_inbound_session(
+ Account & local_account,
+ _olm_curve25519_public_key const * their_identity_key,
+ std::uint8_t const * pre_key_message, std::size_t message_length
+ );
+
+ /** The number of bytes written by session_id() */
+ std::size_t session_id_length() const;
+
+ /** An identifier for this session. Generated by hashing the public keys
+ * used to create the session. Returns the length of the session id on
+ * success or std::size_t(-1) on failure. On failure last_error will be set
+ * with an error code. The last_error will be OUTPUT_BUFFER_TOO_SMALL if
+ * the id buffer was too small. */
+ std::size_t session_id(
+ std::uint8_t * id, std::size_t id_length
+ );
+
+ /** True if this session can be used to decode an inbound pre-key message.
+ * This can be used to test whether a pre-key message should be decoded
+ * with an existing session or if a new session will need to be created.
+ * Returns true if the session is the same. Returns false if either the
+ * session does not match or the pre-key message could not be decoded.
+ */
+ bool matches_inbound_session(
+ _olm_curve25519_public_key const * their_identity_key,
+ std::uint8_t const * pre_key_message, std::size_t message_length
+ ) const;
+
+ /** Whether the next message will be a pre-key message or a normal message.
+ * An outbound session will send pre-key messages until it receives a
+ * message with a ratchet key. */
+ MessageType encrypt_message_type() const;
+
+ std::size_t encrypt_message_length(
+ std::size_t plaintext_length
+ ) const;
+
+ /** The number of bytes of random data the encrypt method will need to
+ * encrypt a message. This will be 32 bytes if the session needs to
+ * generate a new ephemeral key, or will be 0 bytes otherwise. */
+ std::size_t encrypt_random_length() const;
+
+ /** Encrypt some plain-text. Returns the length of the encrypted message
+ * or std::size_t(-1) on failure. On failure last_error will be set with
+ * an error code. The last_error will be NOT_ENOUGH_RANDOM if the number
+ * of random bytes is too small. The last_error will be
+ * OUTPUT_BUFFER_TOO_SMALL if the output buffer is too small. */
+ std::size_t encrypt(
+ std::uint8_t const * plaintext, std::size_t plaintext_length,
+ std::uint8_t const * random, std::size_t random_length,
+ std::uint8_t * message, std::size_t message_length
+ );
+
+ /** An upper bound on the number of bytes of plain-text the decrypt method
+ * will write for a given input message length. */
+ std::size_t decrypt_max_plaintext_length(
+ MessageType message_type,
+ std::uint8_t const * message, std::size_t message_length
+ );
+
+ /** Decrypt a message. Returns the length of the decrypted plain-text or
+ * std::size_t(-1) on failure. On failure last_error will be set with an
+ * error code. The last_error will be OUTPUT_BUFFER_TOO_SMALL if the
+ * plain-text buffer is too small. The last_error will be
+ * BAD_MESSAGE_VERSION if the message was encrypted with an unsupported
+ * version of the protocol. The last_error will be BAD_MESSAGE_FORMAT if
+ * the message headers could not be decoded. The last_error will be
+ * BAD_MESSAGE_MAC if the message could not be verified */
+ std::size_t decrypt(
+ MessageType message_type,
+ std::uint8_t const * message, std::size_t message_length,
+ std::uint8_t * plaintext, std::size_t max_plaintext_length
+ );
+
+ /**
+ * Write a string describing this session and its state (not including the
+ * private key) into the buffer provided.
+ *
+ * Takes a buffer to write to and the length of that buffer
+ */
+ void describe(char *buf, size_t buflen);
+};
+
+
+std::size_t pickle_length(
+ Session const & value
+);
+
+
+std::uint8_t * pickle(
+ std::uint8_t * pos,
+ Session const & value
+);
+
+
+OLM_EXPORT std::uint8_t const * unpickle(
+ std::uint8_t const * pos, std::uint8_t const * end,
+ Session & value
+);
+
+
+} // namespace olm
+
+#endif /* OLM_SESSION_HH_ */
--- /dev/null
+/* Copyright 2015 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef UTILITY_HH_
+#define UTILITY_HH_
+
+#include "olm/error.h"
+
+#include <cstddef>
+#include <cstdint>
+
+struct _olm_ed25519_public_key;
+
+namespace olm {
+
+struct Utility {
+
+ Utility();
+
+ OlmErrorCode last_error;
+
+ /** The length of a SHA-256 hash in bytes. */
+ std::size_t sha256_length() const;
+
+ /** Compute a SHA-256 hash. Returns the length of the SHA-256 hash in bytes
+ * on success. Returns std::size_t(-1) on failure. On failure last_error
+ * will be set with an error code. If the output buffer was too small then
+ * last error will be OUTPUT_BUFFER_TOO_SMALL. */
+ std::size_t sha256(
+ std::uint8_t const * input, std::size_t input_length,
+ std::uint8_t * output, std::size_t output_length
+ );
+
+ /** Verify a ed25519 signature. Returns std::size_t(0) on success. Returns
+ * std::size_t(-1) on failure or if the signature was invalid. On failure
+ * last_error will be set with an error code. If the signature was too short
+ * or was not a valid signature then last_error will be BAD_MESSAGE_MAC. */
+ std::size_t ed25519_verify(
+ _olm_ed25519_public_key const & key,
+ std::uint8_t const * message, std::size_t message_length,
+ std::uint8_t const * signature, std::size_t signature_length
+ );
+
+};
+
+
+} // namespace olm
+
+#endif /* UTILITY_HH_ */
--- /dev/null
+crypto-algorithms
+=================
+
+
+About
+---
+These are basic implementations of standard cryptography algorithms, written by Brad Conte (brad@bradconte.com) from scratch and without any cross-licensing. They exist to provide publically accessible, restriction-free implementations of popular cryptographic algorithms, like AES and SHA-1. These are primarily intended for educational and pragmatic purposes (such as comparing a specification to actual implementation code, or for building an internal application that computes test vectors for a product). The algorithms have been tested against standard test vectors.
+
+This code is released into the public domain free of any restrictions. The author requests acknowledgement if the code is used, but does not require it. This code is provided free of any liability and without any quality claims by the author.
+
+Note that these are *not* cryptographically secure implementations. They have no resistence to side-channel attacks and should not be used in contexts that need cryptographically secure implementations.
+
+These algorithms are not optimized for speed or space. They are primarily designed to be easy to read, although some basic optimization techniques have been employed.
+
+Building
+---
+The source code for each algorithm will come in a pair of a source code file and a header file. There should be no inter-header file dependencies, no additional libraries, no platform-specific header files, or any other complicating matters. Compiling them should be as easy as adding the relevent source code to the project.
\ No newline at end of file
--- /dev/null
+/*********************************************************************
+* Filename: aes.c
+* Author: Brad Conte (brad AT bradconte.com)
+* Copyright:
+* Disclaimer: This code is presented "as is" without any guarantees.
+* Details: This code is the implementation of the AES algorithm and
+ the CTR, CBC, and CCM modes of operation it can be used in.
+ AES is, specified by the NIST in in publication FIPS PUB 197,
+ availible at:
+ * http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf .
+ The CBC and CTR modes of operation are specified by
+ NIST SP 800-38 A, available at:
+ * http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf .
+ The CCM mode of operation is specified by NIST SP80-38 C, available at:
+ * http://csrc.nist.gov/publications/nistpubs/800-38C/SP800-38C_updated-July20_2007.pdf
+*********************************************************************/
+
+/*************************** HEADER FILES ***************************/
+#include <stdlib.h>
+#include <memory.h>
+#include "aes.h"
+
+#include <stdio.h>
+#include <string.h>
+
+/****************************** MACROS ******************************/
+// The least significant byte of the word is rotated to the end.
+#define KE_ROTWORD(x) (((x) << 8) | ((x) >> 24))
+
+#define TRUE 1
+#define FALSE 0
+
+/**************************** DATA TYPES ****************************/
+#define AES_128_ROUNDS 10
+#define AES_192_ROUNDS 12
+#define AES_256_ROUNDS 14
+
+/*********************** FUNCTION DECLARATIONS **********************/
+void ccm_prepare_first_ctr_blk(BYTE counter[], const BYTE nonce[], int nonce_len, int payload_len_store_size);
+void ccm_prepare_first_format_blk(BYTE buf[], int assoc_len, int payload_len, int payload_len_store_size, int mac_len, const BYTE nonce[], int nonce_len);
+void ccm_format_assoc_data(BYTE buf[], int *end_of_buf, const BYTE assoc[], int assoc_len);
+void ccm_format_payload_data(BYTE buf[], int *end_of_buf, const BYTE payload[], int payload_len);
+
+/**************************** VARIABLES *****************************/
+// This is the specified AES SBox. To look up a substitution value, put the first
+// nibble in the first index (row) and the second nibble in the second index (column).
+static const BYTE _olm_aes_sbox[16][16] = {
+ {0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5,0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76},
+ {0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0,0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0},
+ {0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC,0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15},
+ {0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A,0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75},
+ {0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0,0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84},
+ {0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B,0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF},
+ {0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85,0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8},
+ {0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5,0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2},
+ {0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17,0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73},
+ {0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88,0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB},
+ {0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C,0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79},
+ {0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9,0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08},
+ {0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6,0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A},
+ {0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E,0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E},
+ {0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94,0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF},
+ {0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68,0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16}
+};
+
+static const BYTE _olm_aes_invsbox[16][16] = {
+ {0x52,0x09,0x6A,0xD5,0x30,0x36,0xA5,0x38,0xBF,0x40,0xA3,0x9E,0x81,0xF3,0xD7,0xFB},
+ {0x7C,0xE3,0x39,0x82,0x9B,0x2F,0xFF,0x87,0x34,0x8E,0x43,0x44,0xC4,0xDE,0xE9,0xCB},
+ {0x54,0x7B,0x94,0x32,0xA6,0xC2,0x23,0x3D,0xEE,0x4C,0x95,0x0B,0x42,0xFA,0xC3,0x4E},
+ {0x08,0x2E,0xA1,0x66,0x28,0xD9,0x24,0xB2,0x76,0x5B,0xA2,0x49,0x6D,0x8B,0xD1,0x25},
+ {0x72,0xF8,0xF6,0x64,0x86,0x68,0x98,0x16,0xD4,0xA4,0x5C,0xCC,0x5D,0x65,0xB6,0x92},
+ {0x6C,0x70,0x48,0x50,0xFD,0xED,0xB9,0xDA,0x5E,0x15,0x46,0x57,0xA7,0x8D,0x9D,0x84},
+ {0x90,0xD8,0xAB,0x00,0x8C,0xBC,0xD3,0x0A,0xF7,0xE4,0x58,0x05,0xB8,0xB3,0x45,0x06},
+ {0xD0,0x2C,0x1E,0x8F,0xCA,0x3F,0x0F,0x02,0xC1,0xAF,0xBD,0x03,0x01,0x13,0x8A,0x6B},
+ {0x3A,0x91,0x11,0x41,0x4F,0x67,0xDC,0xEA,0x97,0xF2,0xCF,0xCE,0xF0,0xB4,0xE6,0x73},
+ {0x96,0xAC,0x74,0x22,0xE7,0xAD,0x35,0x85,0xE2,0xF9,0x37,0xE8,0x1C,0x75,0xDF,0x6E},
+ {0x47,0xF1,0x1A,0x71,0x1D,0x29,0xC5,0x89,0x6F,0xB7,0x62,0x0E,0xAA,0x18,0xBE,0x1B},
+ {0xFC,0x56,0x3E,0x4B,0xC6,0xD2,0x79,0x20,0x9A,0xDB,0xC0,0xFE,0x78,0xCD,0x5A,0xF4},
+ {0x1F,0xDD,0xA8,0x33,0x88,0x07,0xC7,0x31,0xB1,0x12,0x10,0x59,0x27,0x80,0xEC,0x5F},
+ {0x60,0x51,0x7F,0xA9,0x19,0xB5,0x4A,0x0D,0x2D,0xE5,0x7A,0x9F,0x93,0xC9,0x9C,0xEF},
+ {0xA0,0xE0,0x3B,0x4D,0xAE,0x2A,0xF5,0xB0,0xC8,0xEB,0xBB,0x3C,0x83,0x53,0x99,0x61},
+ {0x17,0x2B,0x04,0x7E,0xBA,0x77,0xD6,0x26,0xE1,0x69,0x14,0x63,0x55,0x21,0x0C,0x7D}
+};
+
+// This table stores pre-calculated values for all possible GF(2^8) calculations.This
+// table is only used by the (Inv)MixColumns steps.
+// USAGE: The second index (column) is the coefficient of multiplication. Only 7 different
+// coefficients are used: 0x01, 0x02, 0x03, 0x09, 0x0b, 0x0d, 0x0e, but multiplication by
+// 1 is negligible leaving only 6 coefficients. Each column of the table is devoted to one
+// of these coefficients, in the ascending order of value, from values 0x00 to 0xFF.
+static const BYTE gf_mul[256][6] = {
+ {0x00,0x00,0x00,0x00,0x00,0x00},{0x02,0x03,0x09,0x0b,0x0d,0x0e},
+ {0x04,0x06,0x12,0x16,0x1a,0x1c},{0x06,0x05,0x1b,0x1d,0x17,0x12},
+ {0x08,0x0c,0x24,0x2c,0x34,0x38},{0x0a,0x0f,0x2d,0x27,0x39,0x36},
+ {0x0c,0x0a,0x36,0x3a,0x2e,0x24},{0x0e,0x09,0x3f,0x31,0x23,0x2a},
+ {0x10,0x18,0x48,0x58,0x68,0x70},{0x12,0x1b,0x41,0x53,0x65,0x7e},
+ {0x14,0x1e,0x5a,0x4e,0x72,0x6c},{0x16,0x1d,0x53,0x45,0x7f,0x62},
+ {0x18,0x14,0x6c,0x74,0x5c,0x48},{0x1a,0x17,0x65,0x7f,0x51,0x46},
+ {0x1c,0x12,0x7e,0x62,0x46,0x54},{0x1e,0x11,0x77,0x69,0x4b,0x5a},
+ {0x20,0x30,0x90,0xb0,0xd0,0xe0},{0x22,0x33,0x99,0xbb,0xdd,0xee},
+ {0x24,0x36,0x82,0xa6,0xca,0xfc},{0x26,0x35,0x8b,0xad,0xc7,0xf2},
+ {0x28,0x3c,0xb4,0x9c,0xe4,0xd8},{0x2a,0x3f,0xbd,0x97,0xe9,0xd6},
+ {0x2c,0x3a,0xa6,0x8a,0xfe,0xc4},{0x2e,0x39,0xaf,0x81,0xf3,0xca},
+ {0x30,0x28,0xd8,0xe8,0xb8,0x90},{0x32,0x2b,0xd1,0xe3,0xb5,0x9e},
+ {0x34,0x2e,0xca,0xfe,0xa2,0x8c},{0x36,0x2d,0xc3,0xf5,0xaf,0x82},
+ {0x38,0x24,0xfc,0xc4,0x8c,0xa8},{0x3a,0x27,0xf5,0xcf,0x81,0xa6},
+ {0x3c,0x22,0xee,0xd2,0x96,0xb4},{0x3e,0x21,0xe7,0xd9,0x9b,0xba},
+ {0x40,0x60,0x3b,0x7b,0xbb,0xdb},{0x42,0x63,0x32,0x70,0xb6,0xd5},
+ {0x44,0x66,0x29,0x6d,0xa1,0xc7},{0x46,0x65,0x20,0x66,0xac,0xc9},
+ {0x48,0x6c,0x1f,0x57,0x8f,0xe3},{0x4a,0x6f,0x16,0x5c,0x82,0xed},
+ {0x4c,0x6a,0x0d,0x41,0x95,0xff},{0x4e,0x69,0x04,0x4a,0x98,0xf1},
+ {0x50,0x78,0x73,0x23,0xd3,0xab},{0x52,0x7b,0x7a,0x28,0xde,0xa5},
+ {0x54,0x7e,0x61,0x35,0xc9,0xb7},{0x56,0x7d,0x68,0x3e,0xc4,0xb9},
+ {0x58,0x74,0x57,0x0f,0xe7,0x93},{0x5a,0x77,0x5e,0x04,0xea,0x9d},
+ {0x5c,0x72,0x45,0x19,0xfd,0x8f},{0x5e,0x71,0x4c,0x12,0xf0,0x81},
+ {0x60,0x50,0xab,0xcb,0x6b,0x3b},{0x62,0x53,0xa2,0xc0,0x66,0x35},
+ {0x64,0x56,0xb9,0xdd,0x71,0x27},{0x66,0x55,0xb0,0xd6,0x7c,0x29},
+ {0x68,0x5c,0x8f,0xe7,0x5f,0x03},{0x6a,0x5f,0x86,0xec,0x52,0x0d},
+ {0x6c,0x5a,0x9d,0xf1,0x45,0x1f},{0x6e,0x59,0x94,0xfa,0x48,0x11},
+ {0x70,0x48,0xe3,0x93,0x03,0x4b},{0x72,0x4b,0xea,0x98,0x0e,0x45},
+ {0x74,0x4e,0xf1,0x85,0x19,0x57},{0x76,0x4d,0xf8,0x8e,0x14,0x59},
+ {0x78,0x44,0xc7,0xbf,0x37,0x73},{0x7a,0x47,0xce,0xb4,0x3a,0x7d},
+ {0x7c,0x42,0xd5,0xa9,0x2d,0x6f},{0x7e,0x41,0xdc,0xa2,0x20,0x61},
+ {0x80,0xc0,0x76,0xf6,0x6d,0xad},{0x82,0xc3,0x7f,0xfd,0x60,0xa3},
+ {0x84,0xc6,0x64,0xe0,0x77,0xb1},{0x86,0xc5,0x6d,0xeb,0x7a,0xbf},
+ {0x88,0xcc,0x52,0xda,0x59,0x95},{0x8a,0xcf,0x5b,0xd1,0x54,0x9b},
+ {0x8c,0xca,0x40,0xcc,0x43,0x89},{0x8e,0xc9,0x49,0xc7,0x4e,0x87},
+ {0x90,0xd8,0x3e,0xae,0x05,0xdd},{0x92,0xdb,0x37,0xa5,0x08,0xd3},
+ {0x94,0xde,0x2c,0xb8,0x1f,0xc1},{0x96,0xdd,0x25,0xb3,0x12,0xcf},
+ {0x98,0xd4,0x1a,0x82,0x31,0xe5},{0x9a,0xd7,0x13,0x89,0x3c,0xeb},
+ {0x9c,0xd2,0x08,0x94,0x2b,0xf9},{0x9e,0xd1,0x01,0x9f,0x26,0xf7},
+ {0xa0,0xf0,0xe6,0x46,0xbd,0x4d},{0xa2,0xf3,0xef,0x4d,0xb0,0x43},
+ {0xa4,0xf6,0xf4,0x50,0xa7,0x51},{0xa6,0xf5,0xfd,0x5b,0xaa,0x5f},
+ {0xa8,0xfc,0xc2,0x6a,0x89,0x75},{0xaa,0xff,0xcb,0x61,0x84,0x7b},
+ {0xac,0xfa,0xd0,0x7c,0x93,0x69},{0xae,0xf9,0xd9,0x77,0x9e,0x67},
+ {0xb0,0xe8,0xae,0x1e,0xd5,0x3d},{0xb2,0xeb,0xa7,0x15,0xd8,0x33},
+ {0xb4,0xee,0xbc,0x08,0xcf,0x21},{0xb6,0xed,0xb5,0x03,0xc2,0x2f},
+ {0xb8,0xe4,0x8a,0x32,0xe1,0x05},{0xba,0xe7,0x83,0x39,0xec,0x0b},
+ {0xbc,0xe2,0x98,0x24,0xfb,0x19},{0xbe,0xe1,0x91,0x2f,0xf6,0x17},
+ {0xc0,0xa0,0x4d,0x8d,0xd6,0x76},{0xc2,0xa3,0x44,0x86,0xdb,0x78},
+ {0xc4,0xa6,0x5f,0x9b,0xcc,0x6a},{0xc6,0xa5,0x56,0x90,0xc1,0x64},
+ {0xc8,0xac,0x69,0xa1,0xe2,0x4e},{0xca,0xaf,0x60,0xaa,0xef,0x40},
+ {0xcc,0xaa,0x7b,0xb7,0xf8,0x52},{0xce,0xa9,0x72,0xbc,0xf5,0x5c},
+ {0xd0,0xb8,0x05,0xd5,0xbe,0x06},{0xd2,0xbb,0x0c,0xde,0xb3,0x08},
+ {0xd4,0xbe,0x17,0xc3,0xa4,0x1a},{0xd6,0xbd,0x1e,0xc8,0xa9,0x14},
+ {0xd8,0xb4,0x21,0xf9,0x8a,0x3e},{0xda,0xb7,0x28,0xf2,0x87,0x30},
+ {0xdc,0xb2,0x33,0xef,0x90,0x22},{0xde,0xb1,0x3a,0xe4,0x9d,0x2c},
+ {0xe0,0x90,0xdd,0x3d,0x06,0x96},{0xe2,0x93,0xd4,0x36,0x0b,0x98},
+ {0xe4,0x96,0xcf,0x2b,0x1c,0x8a},{0xe6,0x95,0xc6,0x20,0x11,0x84},
+ {0xe8,0x9c,0xf9,0x11,0x32,0xae},{0xea,0x9f,0xf0,0x1a,0x3f,0xa0},
+ {0xec,0x9a,0xeb,0x07,0x28,0xb2},{0xee,0x99,0xe2,0x0c,0x25,0xbc},
+ {0xf0,0x88,0x95,0x65,0x6e,0xe6},{0xf2,0x8b,0x9c,0x6e,0x63,0xe8},
+ {0xf4,0x8e,0x87,0x73,0x74,0xfa},{0xf6,0x8d,0x8e,0x78,0x79,0xf4},
+ {0xf8,0x84,0xb1,0x49,0x5a,0xde},{0xfa,0x87,0xb8,0x42,0x57,0xd0},
+ {0xfc,0x82,0xa3,0x5f,0x40,0xc2},{0xfe,0x81,0xaa,0x54,0x4d,0xcc},
+ {0x1b,0x9b,0xec,0xf7,0xda,0x41},{0x19,0x98,0xe5,0xfc,0xd7,0x4f},
+ {0x1f,0x9d,0xfe,0xe1,0xc0,0x5d},{0x1d,0x9e,0xf7,0xea,0xcd,0x53},
+ {0x13,0x97,0xc8,0xdb,0xee,0x79},{0x11,0x94,0xc1,0xd0,0xe3,0x77},
+ {0x17,0x91,0xda,0xcd,0xf4,0x65},{0x15,0x92,0xd3,0xc6,0xf9,0x6b},
+ {0x0b,0x83,0xa4,0xaf,0xb2,0x31},{0x09,0x80,0xad,0xa4,0xbf,0x3f},
+ {0x0f,0x85,0xb6,0xb9,0xa8,0x2d},{0x0d,0x86,0xbf,0xb2,0xa5,0x23},
+ {0x03,0x8f,0x80,0x83,0x86,0x09},{0x01,0x8c,0x89,0x88,0x8b,0x07},
+ {0x07,0x89,0x92,0x95,0x9c,0x15},{0x05,0x8a,0x9b,0x9e,0x91,0x1b},
+ {0x3b,0xab,0x7c,0x47,0x0a,0xa1},{0x39,0xa8,0x75,0x4c,0x07,0xaf},
+ {0x3f,0xad,0x6e,0x51,0x10,0xbd},{0x3d,0xae,0x67,0x5a,0x1d,0xb3},
+ {0x33,0xa7,0x58,0x6b,0x3e,0x99},{0x31,0xa4,0x51,0x60,0x33,0x97},
+ {0x37,0xa1,0x4a,0x7d,0x24,0x85},{0x35,0xa2,0x43,0x76,0x29,0x8b},
+ {0x2b,0xb3,0x34,0x1f,0x62,0xd1},{0x29,0xb0,0x3d,0x14,0x6f,0xdf},
+ {0x2f,0xb5,0x26,0x09,0x78,0xcd},{0x2d,0xb6,0x2f,0x02,0x75,0xc3},
+ {0x23,0xbf,0x10,0x33,0x56,0xe9},{0x21,0xbc,0x19,0x38,0x5b,0xe7},
+ {0x27,0xb9,0x02,0x25,0x4c,0xf5},{0x25,0xba,0x0b,0x2e,0x41,0xfb},
+ {0x5b,0xfb,0xd7,0x8c,0x61,0x9a},{0x59,0xf8,0xde,0x87,0x6c,0x94},
+ {0x5f,0xfd,0xc5,0x9a,0x7b,0x86},{0x5d,0xfe,0xcc,0x91,0x76,0x88},
+ {0x53,0xf7,0xf3,0xa0,0x55,0xa2},{0x51,0xf4,0xfa,0xab,0x58,0xac},
+ {0x57,0xf1,0xe1,0xb6,0x4f,0xbe},{0x55,0xf2,0xe8,0xbd,0x42,0xb0},
+ {0x4b,0xe3,0x9f,0xd4,0x09,0xea},{0x49,0xe0,0x96,0xdf,0x04,0xe4},
+ {0x4f,0xe5,0x8d,0xc2,0x13,0xf6},{0x4d,0xe6,0x84,0xc9,0x1e,0xf8},
+ {0x43,0xef,0xbb,0xf8,0x3d,0xd2},{0x41,0xec,0xb2,0xf3,0x30,0xdc},
+ {0x47,0xe9,0xa9,0xee,0x27,0xce},{0x45,0xea,0xa0,0xe5,0x2a,0xc0},
+ {0x7b,0xcb,0x47,0x3c,0xb1,0x7a},{0x79,0xc8,0x4e,0x37,0xbc,0x74},
+ {0x7f,0xcd,0x55,0x2a,0xab,0x66},{0x7d,0xce,0x5c,0x21,0xa6,0x68},
+ {0x73,0xc7,0x63,0x10,0x85,0x42},{0x71,0xc4,0x6a,0x1b,0x88,0x4c},
+ {0x77,0xc1,0x71,0x06,0x9f,0x5e},{0x75,0xc2,0x78,0x0d,0x92,0x50},
+ {0x6b,0xd3,0x0f,0x64,0xd9,0x0a},{0x69,0xd0,0x06,0x6f,0xd4,0x04},
+ {0x6f,0xd5,0x1d,0x72,0xc3,0x16},{0x6d,0xd6,0x14,0x79,0xce,0x18},
+ {0x63,0xdf,0x2b,0x48,0xed,0x32},{0x61,0xdc,0x22,0x43,0xe0,0x3c},
+ {0x67,0xd9,0x39,0x5e,0xf7,0x2e},{0x65,0xda,0x30,0x55,0xfa,0x20},
+ {0x9b,0x5b,0x9a,0x01,0xb7,0xec},{0x99,0x58,0x93,0x0a,0xba,0xe2},
+ {0x9f,0x5d,0x88,0x17,0xad,0xf0},{0x9d,0x5e,0x81,0x1c,0xa0,0xfe},
+ {0x93,0x57,0xbe,0x2d,0x83,0xd4},{0x91,0x54,0xb7,0x26,0x8e,0xda},
+ {0x97,0x51,0xac,0x3b,0x99,0xc8},{0x95,0x52,0xa5,0x30,0x94,0xc6},
+ {0x8b,0x43,0xd2,0x59,0xdf,0x9c},{0x89,0x40,0xdb,0x52,0xd2,0x92},
+ {0x8f,0x45,0xc0,0x4f,0xc5,0x80},{0x8d,0x46,0xc9,0x44,0xc8,0x8e},
+ {0x83,0x4f,0xf6,0x75,0xeb,0xa4},{0x81,0x4c,0xff,0x7e,0xe6,0xaa},
+ {0x87,0x49,0xe4,0x63,0xf1,0xb8},{0x85,0x4a,0xed,0x68,0xfc,0xb6},
+ {0xbb,0x6b,0x0a,0xb1,0x67,0x0c},{0xb9,0x68,0x03,0xba,0x6a,0x02},
+ {0xbf,0x6d,0x18,0xa7,0x7d,0x10},{0xbd,0x6e,0x11,0xac,0x70,0x1e},
+ {0xb3,0x67,0x2e,0x9d,0x53,0x34},{0xb1,0x64,0x27,0x96,0x5e,0x3a},
+ {0xb7,0x61,0x3c,0x8b,0x49,0x28},{0xb5,0x62,0x35,0x80,0x44,0x26},
+ {0xab,0x73,0x42,0xe9,0x0f,0x7c},{0xa9,0x70,0x4b,0xe2,0x02,0x72},
+ {0xaf,0x75,0x50,0xff,0x15,0x60},{0xad,0x76,0x59,0xf4,0x18,0x6e},
+ {0xa3,0x7f,0x66,0xc5,0x3b,0x44},{0xa1,0x7c,0x6f,0xce,0x36,0x4a},
+ {0xa7,0x79,0x74,0xd3,0x21,0x58},{0xa5,0x7a,0x7d,0xd8,0x2c,0x56},
+ {0xdb,0x3b,0xa1,0x7a,0x0c,0x37},{0xd9,0x38,0xa8,0x71,0x01,0x39},
+ {0xdf,0x3d,0xb3,0x6c,0x16,0x2b},{0xdd,0x3e,0xba,0x67,0x1b,0x25},
+ {0xd3,0x37,0x85,0x56,0x38,0x0f},{0xd1,0x34,0x8c,0x5d,0x35,0x01},
+ {0xd7,0x31,0x97,0x40,0x22,0x13},{0xd5,0x32,0x9e,0x4b,0x2f,0x1d},
+ {0xcb,0x23,0xe9,0x22,0x64,0x47},{0xc9,0x20,0xe0,0x29,0x69,0x49},
+ {0xcf,0x25,0xfb,0x34,0x7e,0x5b},{0xcd,0x26,0xf2,0x3f,0x73,0x55},
+ {0xc3,0x2f,0xcd,0x0e,0x50,0x7f},{0xc1,0x2c,0xc4,0x05,0x5d,0x71},
+ {0xc7,0x29,0xdf,0x18,0x4a,0x63},{0xc5,0x2a,0xd6,0x13,0x47,0x6d},
+ {0xfb,0x0b,0x31,0xca,0xdc,0xd7},{0xf9,0x08,0x38,0xc1,0xd1,0xd9},
+ {0xff,0x0d,0x23,0xdc,0xc6,0xcb},{0xfd,0x0e,0x2a,0xd7,0xcb,0xc5},
+ {0xf3,0x07,0x15,0xe6,0xe8,0xef},{0xf1,0x04,0x1c,0xed,0xe5,0xe1},
+ {0xf7,0x01,0x07,0xf0,0xf2,0xf3},{0xf5,0x02,0x0e,0xfb,0xff,0xfd},
+ {0xeb,0x13,0x79,0x92,0xb4,0xa7},{0xe9,0x10,0x70,0x99,0xb9,0xa9},
+ {0xef,0x15,0x6b,0x84,0xae,0xbb},{0xed,0x16,0x62,0x8f,0xa3,0xb5},
+ {0xe3,0x1f,0x5d,0xbe,0x80,0x9f},{0xe1,0x1c,0x54,0xb5,0x8d,0x91},
+ {0xe7,0x19,0x4f,0xa8,0x9a,0x83},{0xe5,0x1a,0x46,0xa3,0x97,0x8d}
+};
+
+/*********************** FUNCTION DEFINITIONS ***********************/
+// XORs the in and out buffers, storing the result in out. Length is in bytes.
+void xor_buf(const BYTE in[], BYTE out[], size_t len)
+{
+ size_t idx;
+
+ for (idx = 0; idx < len; idx++)
+ out[idx] ^= in[idx];
+}
+
+/*******************
+* AES - CBC
+*******************/
+int _olm_aes_encrypt_cbc(const BYTE in[], size_t in_len, BYTE out[], const WORD key[], int keysize, const BYTE iv[])
+{
+ BYTE buf_in[AES_BLOCK_SIZE], buf_out[AES_BLOCK_SIZE];
+ int blocks, idx;
+
+ if (in_len % AES_BLOCK_SIZE != 0)
+ return(FALSE);
+
+ blocks = in_len / AES_BLOCK_SIZE;
+
+ memcpy(buf_out, iv, AES_BLOCK_SIZE);
+
+ for (idx = 0; idx < blocks; idx++) {
+ memcpy(buf_in, &in[idx * AES_BLOCK_SIZE], AES_BLOCK_SIZE);
+ xor_buf(buf_out, buf_in, AES_BLOCK_SIZE);
+ _olm_aes_encrypt(buf_in, buf_out, key, keysize);
+ memcpy(&out[idx * AES_BLOCK_SIZE], buf_out, AES_BLOCK_SIZE);
+ }
+
+ return(TRUE);
+}
+
+int _olm_aes_encrypt_cbc_mac(const BYTE in[], size_t in_len, BYTE out[], const WORD key[], int keysize, const BYTE iv[])
+{
+ BYTE buf_in[AES_BLOCK_SIZE], buf_out[AES_BLOCK_SIZE];
+ int blocks, idx;
+
+ if (in_len % AES_BLOCK_SIZE != 0)
+ return(FALSE);
+
+ blocks = in_len / AES_BLOCK_SIZE;
+
+ memcpy(buf_out, iv, AES_BLOCK_SIZE);
+
+ for (idx = 0; idx < blocks; idx++) {
+ memcpy(buf_in, &in[idx * AES_BLOCK_SIZE], AES_BLOCK_SIZE);
+ xor_buf(buf_out, buf_in, AES_BLOCK_SIZE);
+ _olm_aes_encrypt(buf_in, buf_out, key, keysize);
+ // Do not output all encrypted blocks.
+ }
+
+ memcpy(out, buf_out, AES_BLOCK_SIZE); // Only output the last block.
+
+ return(TRUE);
+}
+
+// No need for an _olm_aes_decrypt_cbc() for just CCM.
+
+/*******************
+* AES - CTR
+*******************/
+void increment_iv(BYTE iv[], int counter_size)
+{
+ int idx;
+
+ // Use counter_size bytes at the end of the IV as the big-endian integer to increment.
+ for (idx = AES_BLOCK_SIZE - 1; idx >= AES_BLOCK_SIZE - counter_size; idx--) {
+ iv[idx]++;
+ if (iv[idx] != 0 || idx == AES_BLOCK_SIZE - counter_size)
+ break;
+ }
+}
+
+// Performs the encryption in-place, the input and output buffers may be the same.
+// Input may be an arbitrary length (in bytes).
+void _olm_aes_encrypt_ctr(const BYTE in[], size_t in_len, BYTE out[], const WORD key[], int keysize, const BYTE iv[])
+{
+ size_t idx = 0, last_block_length;
+ BYTE iv_buf[AES_BLOCK_SIZE], out_buf[AES_BLOCK_SIZE];
+
+ if (in != out)
+ memcpy(out, in, in_len);
+
+ memcpy(iv_buf, iv, AES_BLOCK_SIZE);
+ last_block_length = in_len - AES_BLOCK_SIZE;
+
+ if (in_len > AES_BLOCK_SIZE) {
+ for (idx = 0; idx < last_block_length; idx += AES_BLOCK_SIZE) {
+ _olm_aes_encrypt(iv_buf, out_buf, key, keysize);
+ xor_buf(out_buf, &out[idx], AES_BLOCK_SIZE);
+ increment_iv(iv_buf, AES_BLOCK_SIZE);
+ }
+ }
+
+ _olm_aes_encrypt(iv_buf, out_buf, key, keysize);
+ xor_buf(out_buf, &out[idx], in_len - idx); // Use the Most Significant bytes.
+}
+
+void _olm_aes_decrypt_ctr(const BYTE in[], size_t in_len, BYTE out[], const WORD key[], int keysize, const BYTE iv[])
+{
+ // CTR encryption is its own inverse function.
+ _olm_aes_encrypt_ctr(in, in_len, out, key, keysize, iv);
+}
+
+/*******************
+* AES - CCM
+*******************/
+// out_len = payload_len + assoc_len
+int _olm_aes_encrypt_ccm(const BYTE payload[], WORD payload_len, const BYTE assoc[], unsigned short assoc_len,
+ const BYTE nonce[], unsigned short nonce_len, BYTE out[], WORD *out_len,
+ WORD mac_len, const BYTE key_str[], int keysize)
+{
+ BYTE temp_iv[AES_BLOCK_SIZE], counter[AES_BLOCK_SIZE], mac[16], *buf;
+ int end_of_buf, payload_len_store_size;
+ WORD key[60];
+
+ if (mac_len != 4 && mac_len != 6 && mac_len != 8 && mac_len != 10 &&
+ mac_len != 12 && mac_len != 14 && mac_len != 16)
+ return(FALSE);
+
+ if (nonce_len < 7 || nonce_len > 13)
+ return(FALSE);
+
+ if (assoc_len > 32768 /* = 2^15 */)
+ return(FALSE);
+
+ buf = (BYTE*)malloc(payload_len + assoc_len + 48 /*Round both payload and associated data up a block size and add an extra block.*/);
+ if (! buf)
+ return(FALSE);
+
+ // Prepare the key for usage.
+ _olm_aes_key_setup(key_str, key, keysize);
+
+ // Format the first block of the formatted data.
+ payload_len_store_size = AES_BLOCK_SIZE - 1 - nonce_len;
+ ccm_prepare_first_format_blk(buf, assoc_len, payload_len, payload_len_store_size, mac_len, nonce, nonce_len);
+ end_of_buf = AES_BLOCK_SIZE;
+
+ // Format the Associated Data, aka, assoc[].
+ ccm_format_assoc_data(buf, &end_of_buf, assoc, assoc_len);
+
+ // Format the Payload, aka payload[].
+ ccm_format_payload_data(buf, &end_of_buf, payload, payload_len);
+
+ // Create the first counter block.
+ ccm_prepare_first_ctr_blk(counter, nonce, nonce_len, payload_len_store_size);
+
+ // Perform the CBC operation with an IV of zeros on the formatted buffer to calculate the MAC.
+ memset(temp_iv, 0, AES_BLOCK_SIZE);
+ _olm_aes_encrypt_cbc_mac(buf, end_of_buf, mac, key, keysize, temp_iv);
+
+ // Copy the Payload and MAC to the output buffer.
+ memcpy(out, payload, payload_len);
+ memcpy(&out[payload_len], mac, mac_len);
+
+ // Encrypt the Payload with CTR mode with a counter starting at 1.
+ memcpy(temp_iv, counter, AES_BLOCK_SIZE);
+ increment_iv(temp_iv, AES_BLOCK_SIZE - 1 - mac_len); // Last argument is the byte size of the counting portion of the counter block. /*BUG?*/
+ _olm_aes_encrypt_ctr(out, payload_len, out, key, keysize, temp_iv);
+
+ // Encrypt the MAC with CTR mode with a counter starting at 0.
+ _olm_aes_encrypt_ctr(&out[payload_len], mac_len, &out[payload_len], key, keysize, counter);
+
+ free(buf);
+ *out_len = payload_len + mac_len;
+
+ return(TRUE);
+}
+
+// plaintext_len = ciphertext_len - mac_len
+// Needs a flag for whether the MAC matches.
+int _olm_aes_decrypt_ccm(const BYTE ciphertext[], WORD ciphertext_len, const BYTE assoc[], unsigned short assoc_len,
+ const BYTE nonce[], unsigned short nonce_len, BYTE plaintext[], WORD *plaintext_len,
+ WORD mac_len, int *mac_auth, const BYTE key_str[], int keysize)
+{
+ BYTE temp_iv[AES_BLOCK_SIZE], counter[AES_BLOCK_SIZE], mac[16], mac_buf[16], *buf;
+ int end_of_buf, plaintext_len_store_size;
+ WORD key[60];
+
+ if (ciphertext_len <= mac_len)
+ return(FALSE);
+
+ buf = (BYTE*)malloc(assoc_len + ciphertext_len /*ciphertext_len = plaintext_len + mac_len*/ + 48);
+ if (! buf)
+ return(FALSE);
+
+ // Prepare the key for usage.
+ _olm_aes_key_setup(key_str, key, keysize);
+
+ // Copy the plaintext and MAC to the output buffers.
+ *plaintext_len = ciphertext_len - mac_len;
+ plaintext_len_store_size = AES_BLOCK_SIZE - 1 - nonce_len;
+ memcpy(plaintext, ciphertext, *plaintext_len);
+ memcpy(mac, &ciphertext[*plaintext_len], mac_len);
+
+ // Prepare the first counter block for use in decryption.
+ ccm_prepare_first_ctr_blk(counter, nonce, nonce_len, plaintext_len_store_size);
+
+ // Decrypt the Payload with CTR mode with a counter starting at 1.
+ memcpy(temp_iv, counter, AES_BLOCK_SIZE);
+ increment_iv(temp_iv, AES_BLOCK_SIZE - 1 - mac_len); // (AES_BLOCK_SIZE - 1 - mac_len) is the byte size of the counting portion of the counter block.
+ _olm_aes_decrypt_ctr(plaintext, *plaintext_len, plaintext, key, keysize, temp_iv);
+
+ // Setting mac_auth to NULL disables the authentication check.
+ if (mac_auth != NULL) {
+ // Decrypt the MAC with CTR mode with a counter starting at 0.
+ _olm_aes_decrypt_ctr(mac, mac_len, mac, key, keysize, counter);
+
+ // Format the first block of the formatted data.
+ plaintext_len_store_size = AES_BLOCK_SIZE - 1 - nonce_len;
+ ccm_prepare_first_format_blk(buf, assoc_len, *plaintext_len, plaintext_len_store_size, mac_len, nonce, nonce_len);
+ end_of_buf = AES_BLOCK_SIZE;
+
+ // Format the Associated Data into the authentication buffer.
+ ccm_format_assoc_data(buf, &end_of_buf, assoc, assoc_len);
+
+ // Format the Payload into the authentication buffer.
+ ccm_format_payload_data(buf, &end_of_buf, plaintext, *plaintext_len);
+
+ // Perform the CBC operation with an IV of zeros on the formatted buffer to calculate the MAC.
+ memset(temp_iv, 0, AES_BLOCK_SIZE);
+ _olm_aes_encrypt_cbc_mac(buf, end_of_buf, mac_buf, key, keysize, temp_iv);
+
+ // Compare the calculated MAC against the MAC embedded in the ciphertext to see if they are the same.
+ if (! memcmp(mac, mac_buf, mac_len)) {
+ *mac_auth = TRUE;
+ }
+ else {
+ *mac_auth = FALSE;
+ memset(plaintext, 0, *plaintext_len);
+ }
+ }
+
+ free(buf);
+
+ return(TRUE);
+}
+
+// Creates the first counter block. First byte is flags, then the nonce, then the incremented part.
+void ccm_prepare_first_ctr_blk(BYTE counter[], const BYTE nonce[], int nonce_len, int payload_len_store_size)
+{
+ memset(counter, 0, AES_BLOCK_SIZE);
+ counter[0] = (payload_len_store_size - 1) & 0x07;
+ memcpy(&counter[1], nonce, nonce_len);
+}
+
+void ccm_prepare_first_format_blk(BYTE buf[], int assoc_len, int payload_len, int payload_len_store_size, int mac_len, const BYTE nonce[], int nonce_len)
+{
+ // Set the flags for the first byte of the first block.
+ buf[0] = ((((mac_len - 2) / 2) & 0x07) << 3) | ((payload_len_store_size - 1) & 0x07);
+ if (assoc_len > 0)
+ buf[0] += 0x40;
+ // Format the rest of the first block, storing the nonce and the size of the payload.
+ memcpy(&buf[1], nonce, nonce_len);
+ memset(&buf[1 + nonce_len], 0, AES_BLOCK_SIZE - 1 - nonce_len);
+ buf[15] = payload_len & 0x000000FF;
+ buf[14] = (payload_len >> 8) & 0x000000FF;
+}
+
+void ccm_format_assoc_data(BYTE buf[], int *end_of_buf, const BYTE assoc[], int assoc_len)
+{
+ int pad;
+
+ buf[*end_of_buf + 1] = assoc_len & 0x00FF;
+ buf[*end_of_buf] = (assoc_len >> 8) & 0x00FF;
+ *end_of_buf += 2;
+ memcpy(&buf[*end_of_buf], assoc, assoc_len);
+ *end_of_buf += assoc_len;
+ pad = AES_BLOCK_SIZE - (*end_of_buf % AES_BLOCK_SIZE); /*BUG?*/
+ memset(&buf[*end_of_buf], 0, pad);
+ *end_of_buf += pad;
+}
+
+void ccm_format_payload_data(BYTE buf[], int *end_of_buf, const BYTE payload[], int payload_len)
+{
+ int pad;
+
+ memcpy(&buf[*end_of_buf], payload, payload_len);
+ *end_of_buf += payload_len;
+ pad = *end_of_buf % AES_BLOCK_SIZE;
+ if (pad != 0)
+ pad = AES_BLOCK_SIZE - pad;
+ memset(&buf[*end_of_buf], 0, pad);
+ *end_of_buf += pad;
+}
+
+/*******************
+* AES
+*******************/
+/////////////////
+// KEY EXPANSION
+/////////////////
+
+// Substitutes a word using the AES S-Box.
+WORD SubWord(WORD word)
+{
+ unsigned int result;
+
+ result = (int)_olm_aes_sbox[(word >> 4) & 0x0000000F][word & 0x0000000F];
+ result += (int)_olm_aes_sbox[(word >> 12) & 0x0000000F][(word >> 8) & 0x0000000F] << 8;
+ result += (int)_olm_aes_sbox[(word >> 20) & 0x0000000F][(word >> 16) & 0x0000000F] << 16;
+ result += (int)_olm_aes_sbox[(word >> 28) & 0x0000000F][(word >> 24) & 0x0000000F] << 24;
+ return(result);
+}
+
+// Performs the action of generating the keys that will be used in every round of
+// encryption. "key" is the user-supplied input key, "w" is the output key schedule,
+// "keysize" is the length in bits of "key", must be 128, 192, or 256.
+void _olm_aes_key_setup(const BYTE key[], WORD w[], int keysize)
+{
+ int Nb=4,Nr,Nk,idx;
+ WORD temp,Rcon[]={0x01000000,0x02000000,0x04000000,0x08000000,0x10000000,0x20000000,
+ 0x40000000,0x80000000,0x1b000000,0x36000000,0x6c000000,0xd8000000,
+ 0xab000000,0x4d000000,0x9a000000};
+
+ switch (keysize) {
+ case 128: Nr = 10; Nk = 4; break;
+ case 192: Nr = 12; Nk = 6; break;
+ case 256: Nr = 14; Nk = 8; break;
+ default: return;
+ }
+
+ for (idx=0; idx < Nk; ++idx) {
+ w[idx] = ((key[4 * idx]) << 24) | ((key[4 * idx + 1]) << 16) |
+ ((key[4 * idx + 2]) << 8) | ((key[4 * idx + 3]));
+ }
+
+ for (idx = Nk; idx < Nb * (Nr+1); ++idx) {
+ temp = w[idx - 1];
+ if ((idx % Nk) == 0)
+ temp = SubWord(KE_ROTWORD(temp)) ^ Rcon[(idx-1)/Nk];
+ else if (Nk > 6 && (idx % Nk) == 4)
+ temp = SubWord(temp);
+ w[idx] = w[idx-Nk] ^ temp;
+ }
+}
+
+/////////////////
+// ADD ROUND KEY
+/////////////////
+
+// Performs the AddRoundKey step. Each round has its own pre-generated 16-byte key in the
+// form of 4 integers (the "w" array). Each integer is XOR'd by one column of the state.
+// Also performs the job of InvAddRoundKey(); since the function is a simple XOR process,
+// it is its own inverse.
+void AddRoundKey(BYTE state[][4], const WORD w[])
+{
+ BYTE subkey[4];
+
+ // memcpy(subkey,&w[idx],4); // Not accurate for big endian machines
+ // Subkey 1
+ subkey[0] = w[0] >> 24;
+ subkey[1] = w[0] >> 16;
+ subkey[2] = w[0] >> 8;
+ subkey[3] = w[0];
+ state[0][0] ^= subkey[0];
+ state[1][0] ^= subkey[1];
+ state[2][0] ^= subkey[2];
+ state[3][0] ^= subkey[3];
+ // Subkey 2
+ subkey[0] = w[1] >> 24;
+ subkey[1] = w[1] >> 16;
+ subkey[2] = w[1] >> 8;
+ subkey[3] = w[1];
+ state[0][1] ^= subkey[0];
+ state[1][1] ^= subkey[1];
+ state[2][1] ^= subkey[2];
+ state[3][1] ^= subkey[3];
+ // Subkey 3
+ subkey[0] = w[2] >> 24;
+ subkey[1] = w[2] >> 16;
+ subkey[2] = w[2] >> 8;
+ subkey[3] = w[2];
+ state[0][2] ^= subkey[0];
+ state[1][2] ^= subkey[1];
+ state[2][2] ^= subkey[2];
+ state[3][2] ^= subkey[3];
+ // Subkey 4
+ subkey[0] = w[3] >> 24;
+ subkey[1] = w[3] >> 16;
+ subkey[2] = w[3] >> 8;
+ subkey[3] = w[3];
+ state[0][3] ^= subkey[0];
+ state[1][3] ^= subkey[1];
+ state[2][3] ^= subkey[2];
+ state[3][3] ^= subkey[3];
+}
+
+/////////////////
+// (Inv)SubBytes
+/////////////////
+
+// Performs the SubBytes step. All bytes in the state are substituted with a
+// pre-calculated value from a lookup table.
+void SubBytes(BYTE state[][4])
+{
+ state[0][0] = _olm_aes_sbox[state[0][0] >> 4][state[0][0] & 0x0F];
+ state[0][1] = _olm_aes_sbox[state[0][1] >> 4][state[0][1] & 0x0F];
+ state[0][2] = _olm_aes_sbox[state[0][2] >> 4][state[0][2] & 0x0F];
+ state[0][3] = _olm_aes_sbox[state[0][3] >> 4][state[0][3] & 0x0F];
+ state[1][0] = _olm_aes_sbox[state[1][0] >> 4][state[1][0] & 0x0F];
+ state[1][1] = _olm_aes_sbox[state[1][1] >> 4][state[1][1] & 0x0F];
+ state[1][2] = _olm_aes_sbox[state[1][2] >> 4][state[1][2] & 0x0F];
+ state[1][3] = _olm_aes_sbox[state[1][3] >> 4][state[1][3] & 0x0F];
+ state[2][0] = _olm_aes_sbox[state[2][0] >> 4][state[2][0] & 0x0F];
+ state[2][1] = _olm_aes_sbox[state[2][1] >> 4][state[2][1] & 0x0F];
+ state[2][2] = _olm_aes_sbox[state[2][2] >> 4][state[2][2] & 0x0F];
+ state[2][3] = _olm_aes_sbox[state[2][3] >> 4][state[2][3] & 0x0F];
+ state[3][0] = _olm_aes_sbox[state[3][0] >> 4][state[3][0] & 0x0F];
+ state[3][1] = _olm_aes_sbox[state[3][1] >> 4][state[3][1] & 0x0F];
+ state[3][2] = _olm_aes_sbox[state[3][2] >> 4][state[3][2] & 0x0F];
+ state[3][3] = _olm_aes_sbox[state[3][3] >> 4][state[3][3] & 0x0F];
+}
+
+void InvSubBytes(BYTE state[][4])
+{
+ state[0][0] = _olm_aes_invsbox[state[0][0] >> 4][state[0][0] & 0x0F];
+ state[0][1] = _olm_aes_invsbox[state[0][1] >> 4][state[0][1] & 0x0F];
+ state[0][2] = _olm_aes_invsbox[state[0][2] >> 4][state[0][2] & 0x0F];
+ state[0][3] = _olm_aes_invsbox[state[0][3] >> 4][state[0][3] & 0x0F];
+ state[1][0] = _olm_aes_invsbox[state[1][0] >> 4][state[1][0] & 0x0F];
+ state[1][1] = _olm_aes_invsbox[state[1][1] >> 4][state[1][1] & 0x0F];
+ state[1][2] = _olm_aes_invsbox[state[1][2] >> 4][state[1][2] & 0x0F];
+ state[1][3] = _olm_aes_invsbox[state[1][3] >> 4][state[1][3] & 0x0F];
+ state[2][0] = _olm_aes_invsbox[state[2][0] >> 4][state[2][0] & 0x0F];
+ state[2][1] = _olm_aes_invsbox[state[2][1] >> 4][state[2][1] & 0x0F];
+ state[2][2] = _olm_aes_invsbox[state[2][2] >> 4][state[2][2] & 0x0F];
+ state[2][3] = _olm_aes_invsbox[state[2][3] >> 4][state[2][3] & 0x0F];
+ state[3][0] = _olm_aes_invsbox[state[3][0] >> 4][state[3][0] & 0x0F];
+ state[3][1] = _olm_aes_invsbox[state[3][1] >> 4][state[3][1] & 0x0F];
+ state[3][2] = _olm_aes_invsbox[state[3][2] >> 4][state[3][2] & 0x0F];
+ state[3][3] = _olm_aes_invsbox[state[3][3] >> 4][state[3][3] & 0x0F];
+}
+
+/////////////////
+// (Inv)ShiftRows
+/////////////////
+
+// Performs the ShiftRows step. All rows are shifted cylindrically to the left.
+void ShiftRows(BYTE state[][4])
+{
+ int t;
+
+ // Shift left by 1
+ t = state[1][0];
+ state[1][0] = state[1][1];
+ state[1][1] = state[1][2];
+ state[1][2] = state[1][3];
+ state[1][3] = t;
+ // Shift left by 2
+ t = state[2][0];
+ state[2][0] = state[2][2];
+ state[2][2] = t;
+ t = state[2][1];
+ state[2][1] = state[2][3];
+ state[2][3] = t;
+ // Shift left by 3
+ t = state[3][0];
+ state[3][0] = state[3][3];
+ state[3][3] = state[3][2];
+ state[3][2] = state[3][1];
+ state[3][1] = t;
+}
+
+// All rows are shifted cylindrically to the right.
+void InvShiftRows(BYTE state[][4])
+{
+ int t;
+
+ // Shift right by 1
+ t = state[1][3];
+ state[1][3] = state[1][2];
+ state[1][2] = state[1][1];
+ state[1][1] = state[1][0];
+ state[1][0] = t;
+ // Shift right by 2
+ t = state[2][3];
+ state[2][3] = state[2][1];
+ state[2][1] = t;
+ t = state[2][2];
+ state[2][2] = state[2][0];
+ state[2][0] = t;
+ // Shift right by 3
+ t = state[3][3];
+ state[3][3] = state[3][0];
+ state[3][0] = state[3][1];
+ state[3][1] = state[3][2];
+ state[3][2] = t;
+}
+
+/////////////////
+// (Inv)MixColumns
+/////////////////
+
+// Performs the MixColums step. The state is multiplied by itself using matrix
+// multiplication in a Galios Field 2^8. All multiplication is pre-computed in a table.
+// Addition is equivilent to XOR. (Must always make a copy of the column as the original
+// values will be destoyed.)
+void MixColumns(BYTE state[][4])
+{
+ BYTE col[4];
+
+ // Column 1
+ col[0] = state[0][0];
+ col[1] = state[1][0];
+ col[2] = state[2][0];
+ col[3] = state[3][0];
+ state[0][0] = gf_mul[col[0]][0];
+ state[0][0] ^= gf_mul[col[1]][1];
+ state[0][0] ^= col[2];
+ state[0][0] ^= col[3];
+ state[1][0] = col[0];
+ state[1][0] ^= gf_mul[col[1]][0];
+ state[1][0] ^= gf_mul[col[2]][1];
+ state[1][0] ^= col[3];
+ state[2][0] = col[0];
+ state[2][0] ^= col[1];
+ state[2][0] ^= gf_mul[col[2]][0];
+ state[2][0] ^= gf_mul[col[3]][1];
+ state[3][0] = gf_mul[col[0]][1];
+ state[3][0] ^= col[1];
+ state[3][0] ^= col[2];
+ state[3][0] ^= gf_mul[col[3]][0];
+ // Column 2
+ col[0] = state[0][1];
+ col[1] = state[1][1];
+ col[2] = state[2][1];
+ col[3] = state[3][1];
+ state[0][1] = gf_mul[col[0]][0];
+ state[0][1] ^= gf_mul[col[1]][1];
+ state[0][1] ^= col[2];
+ state[0][1] ^= col[3];
+ state[1][1] = col[0];
+ state[1][1] ^= gf_mul[col[1]][0];
+ state[1][1] ^= gf_mul[col[2]][1];
+ state[1][1] ^= col[3];
+ state[2][1] = col[0];
+ state[2][1] ^= col[1];
+ state[2][1] ^= gf_mul[col[2]][0];
+ state[2][1] ^= gf_mul[col[3]][1];
+ state[3][1] = gf_mul[col[0]][1];
+ state[3][1] ^= col[1];
+ state[3][1] ^= col[2];
+ state[3][1] ^= gf_mul[col[3]][0];
+ // Column 3
+ col[0] = state[0][2];
+ col[1] = state[1][2];
+ col[2] = state[2][2];
+ col[3] = state[3][2];
+ state[0][2] = gf_mul[col[0]][0];
+ state[0][2] ^= gf_mul[col[1]][1];
+ state[0][2] ^= col[2];
+ state[0][2] ^= col[3];
+ state[1][2] = col[0];
+ state[1][2] ^= gf_mul[col[1]][0];
+ state[1][2] ^= gf_mul[col[2]][1];
+ state[1][2] ^= col[3];
+ state[2][2] = col[0];
+ state[2][2] ^= col[1];
+ state[2][2] ^= gf_mul[col[2]][0];
+ state[2][2] ^= gf_mul[col[3]][1];
+ state[3][2] = gf_mul[col[0]][1];
+ state[3][2] ^= col[1];
+ state[3][2] ^= col[2];
+ state[3][2] ^= gf_mul[col[3]][0];
+ // Column 4
+ col[0] = state[0][3];
+ col[1] = state[1][3];
+ col[2] = state[2][3];
+ col[3] = state[3][3];
+ state[0][3] = gf_mul[col[0]][0];
+ state[0][3] ^= gf_mul[col[1]][1];
+ state[0][3] ^= col[2];
+ state[0][3] ^= col[3];
+ state[1][3] = col[0];
+ state[1][3] ^= gf_mul[col[1]][0];
+ state[1][3] ^= gf_mul[col[2]][1];
+ state[1][3] ^= col[3];
+ state[2][3] = col[0];
+ state[2][3] ^= col[1];
+ state[2][3] ^= gf_mul[col[2]][0];
+ state[2][3] ^= gf_mul[col[3]][1];
+ state[3][3] = gf_mul[col[0]][1];
+ state[3][3] ^= col[1];
+ state[3][3] ^= col[2];
+ state[3][3] ^= gf_mul[col[3]][0];
+}
+
+void InvMixColumns(BYTE state[][4])
+{
+ BYTE col[4];
+
+ // Column 1
+ col[0] = state[0][0];
+ col[1] = state[1][0];
+ col[2] = state[2][0];
+ col[3] = state[3][0];
+ state[0][0] = gf_mul[col[0]][5];
+ state[0][0] ^= gf_mul[col[1]][3];
+ state[0][0] ^= gf_mul[col[2]][4];
+ state[0][0] ^= gf_mul[col[3]][2];
+ state[1][0] = gf_mul[col[0]][2];
+ state[1][0] ^= gf_mul[col[1]][5];
+ state[1][0] ^= gf_mul[col[2]][3];
+ state[1][0] ^= gf_mul[col[3]][4];
+ state[2][0] = gf_mul[col[0]][4];
+ state[2][0] ^= gf_mul[col[1]][2];
+ state[2][0] ^= gf_mul[col[2]][5];
+ state[2][0] ^= gf_mul[col[3]][3];
+ state[3][0] = gf_mul[col[0]][3];
+ state[3][0] ^= gf_mul[col[1]][4];
+ state[3][0] ^= gf_mul[col[2]][2];
+ state[3][0] ^= gf_mul[col[3]][5];
+ // Column 2
+ col[0] = state[0][1];
+ col[1] = state[1][1];
+ col[2] = state[2][1];
+ col[3] = state[3][1];
+ state[0][1] = gf_mul[col[0]][5];
+ state[0][1] ^= gf_mul[col[1]][3];
+ state[0][1] ^= gf_mul[col[2]][4];
+ state[0][1] ^= gf_mul[col[3]][2];
+ state[1][1] = gf_mul[col[0]][2];
+ state[1][1] ^= gf_mul[col[1]][5];
+ state[1][1] ^= gf_mul[col[2]][3];
+ state[1][1] ^= gf_mul[col[3]][4];
+ state[2][1] = gf_mul[col[0]][4];
+ state[2][1] ^= gf_mul[col[1]][2];
+ state[2][1] ^= gf_mul[col[2]][5];
+ state[2][1] ^= gf_mul[col[3]][3];
+ state[3][1] = gf_mul[col[0]][3];
+ state[3][1] ^= gf_mul[col[1]][4];
+ state[3][1] ^= gf_mul[col[2]][2];
+ state[3][1] ^= gf_mul[col[3]][5];
+ // Column 3
+ col[0] = state[0][2];
+ col[1] = state[1][2];
+ col[2] = state[2][2];
+ col[3] = state[3][2];
+ state[0][2] = gf_mul[col[0]][5];
+ state[0][2] ^= gf_mul[col[1]][3];
+ state[0][2] ^= gf_mul[col[2]][4];
+ state[0][2] ^= gf_mul[col[3]][2];
+ state[1][2] = gf_mul[col[0]][2];
+ state[1][2] ^= gf_mul[col[1]][5];
+ state[1][2] ^= gf_mul[col[2]][3];
+ state[1][2] ^= gf_mul[col[3]][4];
+ state[2][2] = gf_mul[col[0]][4];
+ state[2][2] ^= gf_mul[col[1]][2];
+ state[2][2] ^= gf_mul[col[2]][5];
+ state[2][2] ^= gf_mul[col[3]][3];
+ state[3][2] = gf_mul[col[0]][3];
+ state[3][2] ^= gf_mul[col[1]][4];
+ state[3][2] ^= gf_mul[col[2]][2];
+ state[3][2] ^= gf_mul[col[3]][5];
+ // Column 4
+ col[0] = state[0][3];
+ col[1] = state[1][3];
+ col[2] = state[2][3];
+ col[3] = state[3][3];
+ state[0][3] = gf_mul[col[0]][5];
+ state[0][3] ^= gf_mul[col[1]][3];
+ state[0][3] ^= gf_mul[col[2]][4];
+ state[0][3] ^= gf_mul[col[3]][2];
+ state[1][3] = gf_mul[col[0]][2];
+ state[1][3] ^= gf_mul[col[1]][5];
+ state[1][3] ^= gf_mul[col[2]][3];
+ state[1][3] ^= gf_mul[col[3]][4];
+ state[2][3] = gf_mul[col[0]][4];
+ state[2][3] ^= gf_mul[col[1]][2];
+ state[2][3] ^= gf_mul[col[2]][5];
+ state[2][3] ^= gf_mul[col[3]][3];
+ state[3][3] = gf_mul[col[0]][3];
+ state[3][3] ^= gf_mul[col[1]][4];
+ state[3][3] ^= gf_mul[col[2]][2];
+ state[3][3] ^= gf_mul[col[3]][5];
+}
+
+/////////////////
+// (En/De)Crypt
+/////////////////
+
+void _olm_aes_encrypt(const BYTE in[], BYTE out[], const WORD key[], int keysize)
+{
+ BYTE state[4][4];
+
+ // Copy input array (should be 16 bytes long) to a matrix (sequential bytes are ordered
+ // by row, not col) called "state" for processing.
+ // *** Implementation note: The official AES documentation references the state by
+ // column, then row. Accessing an element in C requires row then column. Thus, all state
+ // references in AES must have the column and row indexes reversed for C implementation.
+ state[0][0] = in[0];
+ state[1][0] = in[1];
+ state[2][0] = in[2];
+ state[3][0] = in[3];
+ state[0][1] = in[4];
+ state[1][1] = in[5];
+ state[2][1] = in[6];
+ state[3][1] = in[7];
+ state[0][2] = in[8];
+ state[1][2] = in[9];
+ state[2][2] = in[10];
+ state[3][2] = in[11];
+ state[0][3] = in[12];
+ state[1][3] = in[13];
+ state[2][3] = in[14];
+ state[3][3] = in[15];
+
+ // Perform the necessary number of rounds. The round key is added first.
+ // The last round does not perform the MixColumns step.
+ AddRoundKey(state,&key[0]);
+ SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[4]);
+ SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[8]);
+ SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[12]);
+ SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[16]);
+ SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[20]);
+ SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[24]);
+ SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[28]);
+ SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[32]);
+ SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[36]);
+ if (keysize != 128) {
+ SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[40]);
+ SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[44]);
+ if (keysize != 192) {
+ SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[48]);
+ SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[52]);
+ SubBytes(state); ShiftRows(state); AddRoundKey(state,&key[56]);
+ }
+ else {
+ SubBytes(state); ShiftRows(state); AddRoundKey(state,&key[48]);
+ }
+ }
+ else {
+ SubBytes(state); ShiftRows(state); AddRoundKey(state,&key[40]);
+ }
+
+ // Copy the state to the output array.
+ out[0] = state[0][0];
+ out[1] = state[1][0];
+ out[2] = state[2][0];
+ out[3] = state[3][0];
+ out[4] = state[0][1];
+ out[5] = state[1][1];
+ out[6] = state[2][1];
+ out[7] = state[3][1];
+ out[8] = state[0][2];
+ out[9] = state[1][2];
+ out[10] = state[2][2];
+ out[11] = state[3][2];
+ out[12] = state[0][3];
+ out[13] = state[1][3];
+ out[14] = state[2][3];
+ out[15] = state[3][3];
+}
+
+void _olm_aes_decrypt(const BYTE in[], BYTE out[], const WORD key[], int keysize)
+{
+ BYTE state[4][4];
+
+ // Copy the input to the state.
+ state[0][0] = in[0];
+ state[1][0] = in[1];
+ state[2][0] = in[2];
+ state[3][0] = in[3];
+ state[0][1] = in[4];
+ state[1][1] = in[5];
+ state[2][1] = in[6];
+ state[3][1] = in[7];
+ state[0][2] = in[8];
+ state[1][2] = in[9];
+ state[2][2] = in[10];
+ state[3][2] = in[11];
+ state[0][3] = in[12];
+ state[1][3] = in[13];
+ state[2][3] = in[14];
+ state[3][3] = in[15];
+
+ // Perform the necessary number of rounds. The round key is added first.
+ // The last round does not perform the MixColumns step.
+ if (keysize > 128) {
+ if (keysize > 192) {
+ AddRoundKey(state,&key[56]);
+ InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[52]);InvMixColumns(state);
+ InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[48]);InvMixColumns(state);
+ }
+ else {
+ AddRoundKey(state,&key[48]);
+ }
+ InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[44]);InvMixColumns(state);
+ InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[40]);InvMixColumns(state);
+ }
+ else {
+ AddRoundKey(state,&key[40]);
+ }
+ InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[36]);InvMixColumns(state);
+ InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[32]);InvMixColumns(state);
+ InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[28]);InvMixColumns(state);
+ InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[24]);InvMixColumns(state);
+ InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[20]);InvMixColumns(state);
+ InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[16]);InvMixColumns(state);
+ InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[12]);InvMixColumns(state);
+ InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[8]);InvMixColumns(state);
+ InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[4]);InvMixColumns(state);
+ InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[0]);
+
+ // Copy the state to the output array.
+ out[0] = state[0][0];
+ out[1] = state[1][0];
+ out[2] = state[2][0];
+ out[3] = state[3][0];
+ out[4] = state[0][1];
+ out[5] = state[1][1];
+ out[6] = state[2][1];
+ out[7] = state[3][1];
+ out[8] = state[0][2];
+ out[9] = state[1][2];
+ out[10] = state[2][2];
+ out[11] = state[3][2];
+ out[12] = state[0][3];
+ out[13] = state[1][3];
+ out[14] = state[2][3];
+ out[15] = state[3][3];
+}
+
+/*******************
+** AES DEBUGGING FUNCTIONS
+*******************/
+/*
+// This prints the "state" grid as a linear hex string.
+void print_state(BYTE state[][4])
+{
+ int idx,idx2;
+
+ for (idx=0; idx < 4; idx++)
+ for (idx2=0; idx2 < 4; idx2++)
+ printf("%02x",state[idx2][idx]);
+ printf("\n");
+}
+
+// This prints the key (4 consecutive ints) used for a given round as a linear hex string.
+void print_rnd_key(WORD key[])
+{
+ int idx;
+
+ for (idx=0; idx < 4; idx++)
+ printf("%08x",key[idx]);
+ printf("\n");
+}
+*/
--- /dev/null
+/*********************************************************************
+* Filename: aes.h
+* Author: Brad Conte (brad AT bradconte.com)
+* Copyright:
+* Disclaimer: This code is presented "as is" without any guarantees.
+* Details: Defines the API for the corresponding AES implementation.
+*********************************************************************/
+
+#ifndef AES_H
+#define AES_H
+
+/*************************** HEADER FILES ***************************/
+#include <stddef.h>
+
+/****************************** MACROS ******************************/
+#define AES_BLOCK_SIZE 16 // AES operates on 16 bytes at a time
+
+/**************************** DATA TYPES ****************************/
+typedef unsigned char BYTE; // 8-bit byte
+typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines
+
+/*********************** FUNCTION DECLARATIONS **********************/
+///////////////////
+// AES
+///////////////////
+// Key setup must be done before any AES en/de-cryption functions can be used.
+void _olm_aes_key_setup(const BYTE key[], // The key, must be 128, 192, or 256 bits
+ WORD w[], // Output key schedule to be used later
+ int keysize); // Bit length of the key, 128, 192, or 256
+
+void _olm_aes_encrypt(const BYTE in[], // 16 bytes of plaintext
+ BYTE out[], // 16 bytes of ciphertext
+ const WORD key[], // From the key setup
+ int keysize); // Bit length of the key, 128, 192, or 256
+
+void _olm_aes_decrypt(const BYTE in[], // 16 bytes of ciphertext
+ BYTE out[], // 16 bytes of plaintext
+ const WORD key[], // From the key setup
+ int keysize); // Bit length of the key, 128, 192, or 256
+
+///////////////////
+// AES - CBC
+///////////////////
+int _olm_aes_encrypt_cbc(const BYTE in[], // Plaintext
+ size_t in_len, // Must be a multiple of AES_BLOCK_SIZE
+ BYTE out[], // Ciphertext, same length as plaintext
+ const WORD key[], // From the key setup
+ int keysize, // Bit length of the key, 128, 192, or 256
+ const BYTE iv[]); // IV, must be AES_BLOCK_SIZE bytes long
+
+// Only output the CBC-MAC of the input.
+int _olm_aes_encrypt_cbc_mac(const BYTE in[], // plaintext
+ size_t in_len, // Must be a multiple of AES_BLOCK_SIZE
+ BYTE out[], // Output MAC
+ const WORD key[], // From the key setup
+ int keysize, // Bit length of the key, 128, 192, or 256
+ const BYTE iv[]); // IV, must be AES_BLOCK_SIZE bytes long
+
+///////////////////
+// AES - CTR
+///////////////////
+void increment_iv(BYTE iv[], // Must be a multiple of AES_BLOCK_SIZE
+ int counter_size); // Bytes of the IV used for counting (low end)
+
+void _olm_aes_encrypt_ctr(const BYTE in[], // Plaintext
+ size_t in_len, // Any byte length
+ BYTE out[], // Ciphertext, same length as plaintext
+ const WORD key[], // From the key setup
+ int keysize, // Bit length of the key, 128, 192, or 256
+ const BYTE iv[]); // IV, must be AES_BLOCK_SIZE bytes long
+
+void _olm_aes_decrypt_ctr(const BYTE in[], // Ciphertext
+ size_t in_len, // Any byte length
+ BYTE out[], // Plaintext, same length as ciphertext
+ const WORD key[], // From the key setup
+ int keysize, // Bit length of the key, 128, 192, or 256
+ const BYTE iv[]); // IV, must be AES_BLOCK_SIZE bytes long
+
+///////////////////
+// AES - CCM
+///////////////////
+// Returns True if the input parameters do not violate any constraint.
+int _olm_aes_encrypt_ccm(const BYTE plaintext[], // IN - Plaintext.
+ WORD plaintext_len, // IN - Plaintext length.
+ const BYTE associated_data[], // IN - Associated Data included in authentication, but not encryption.
+ unsigned short associated_data_len, // IN - Associated Data length in bytes.
+ const BYTE nonce[], // IN - The Nonce to be used for encryption.
+ unsigned short nonce_len, // IN - Nonce length in bytes.
+ BYTE ciphertext[], // OUT - Ciphertext, a concatination of the plaintext and the MAC.
+ WORD *ciphertext_len, // OUT - The length of the ciphertext, always plaintext_len + mac_len.
+ WORD mac_len, // IN - The desired length of the MAC, must be 4, 6, 8, 10, 12, 14, or 16.
+ const BYTE key[], // IN - The AES key for encryption.
+ int keysize); // IN - The length of the key in bits. Valid values are 128, 192, 256.
+
+// Returns True if the input parameters do not violate any constraint.
+// Use mac_auth to ensure decryption/validation was preformed correctly.
+// If authentication does not succeed, the plaintext is zeroed out. To overwride
+// this, call with mac_auth = NULL. The proper proceedure is to decrypt with
+// authentication enabled (mac_auth != NULL) and make a second call to that
+// ignores authentication explicitly if the first call failes.
+int _olm_aes_decrypt_ccm(const BYTE ciphertext[], // IN - Ciphertext, the concatination of encrypted plaintext and MAC.
+ WORD ciphertext_len, // IN - Ciphertext length in bytes.
+ const BYTE assoc[], // IN - The Associated Data, required for authentication.
+ unsigned short assoc_len, // IN - Associated Data length in bytes.
+ const BYTE nonce[], // IN - The Nonce to use for decryption, same one as for encryption.
+ unsigned short nonce_len, // IN - Nonce length in bytes.
+ BYTE plaintext[], // OUT - The plaintext that was decrypted. Will need to be large enough to hold ciphertext_len - mac_len.
+ WORD *plaintext_len, // OUT - Length in bytes of the output plaintext, always ciphertext_len - mac_len .
+ WORD mac_len, // IN - The length of the MAC that was calculated.
+ int *mac_auth, // OUT - TRUE if authentication succeeded, FALSE if it did not. NULL pointer will ignore the authentication.
+ const BYTE key[], // IN - The AES key for decryption.
+ int keysize); // IN - The length of the key in BITS. Valid values are 128, 192, 256.
+
+///////////////////
+// Test functions
+///////////////////
+int aes_test();
+int aes_ecb_test();
+int aes_cbc_test();
+int aes_ctr_test();
+int aes_ccm_test();
+
+#endif // AES_H
--- /dev/null
+/*********************************************************************
+* Filename: aes_test.c
+* Author: Brad Conte (brad AT bradconte.com)
+* Copyright:
+* Disclaimer: This code is presented "as is" without any guarantees.
+* Details: Performs known-answer tests on the corresponding AES
+ implementation. These tests do not encompass the full
+ range of available test vectors and are not sufficient
+ for FIPS-140 certification. However, if the tests pass
+ it is very, very likely that the code is correct and was
+ compiled properly. This code also serves as
+ example usage of the functions.
+*********************************************************************/
+
+/*************************** HEADER FILES ***************************/
+#include <stdio.h>
+#include <memory.h>
+#include "aes.h"
+
+/*********************** FUNCTION DEFINITIONS ***********************/
+void print_hex(BYTE str[], int len)
+{
+ int idx;
+
+ for(idx = 0; idx < len; idx++)
+ printf("%02x", str[idx]);
+}
+
+int aes_ecb_test()
+{
+ WORD key_schedule[60], idx;
+ BYTE enc_buf[128];
+ BYTE plaintext[2][16] = {
+ {0x6b,0xc1,0xbe,0xe2,0x2e,0x40,0x9f,0x96,0xe9,0x3d,0x7e,0x11,0x73,0x93,0x17,0x2a},
+ {0xae,0x2d,0x8a,0x57,0x1e,0x03,0xac,0x9c,0x9e,0xb7,0x6f,0xac,0x45,0xaf,0x8e,0x51}
+ };
+ BYTE ciphertext[2][16] = {
+ {0xf3,0xee,0xd1,0xbd,0xb5,0xd2,0xa0,0x3c,0x06,0x4b,0x5a,0x7e,0x3d,0xb1,0x81,0xf8},
+ {0x59,0x1c,0xcb,0x10,0xd4,0x10,0xed,0x26,0xdc,0x5b,0xa7,0x4a,0x31,0x36,0x28,0x70}
+ };
+ BYTE key[1][32] = {
+ {0x60,0x3d,0xeb,0x10,0x15,0xca,0x71,0xbe,0x2b,0x73,0xae,0xf0,0x85,0x7d,0x77,0x81,0x1f,0x35,0x2c,0x07,0x3b,0x61,0x08,0xd7,0x2d,0x98,0x10,0xa3,0x09,0x14,0xdf,0xf4}
+ };
+ int pass = 1;
+
+ // Raw ECB mode.
+ //printf("* ECB mode:\n");
+ aes_key_setup(key[0], key_schedule, 256);
+ //printf( "Key : ");
+ //print_hex(key[0], 32);
+
+ for(idx = 0; idx < 2; idx++) {
+ aes_encrypt(plaintext[idx], enc_buf, key_schedule, 256);
+ //printf("\nPlaintext : ");
+ //print_hex(plaintext[idx], 16);
+ //printf("\n-encrypted to: ");
+ //print_hex(enc_buf, 16);
+ pass = pass && !memcmp(enc_buf, ciphertext[idx], 16);
+
+ aes_decrypt(ciphertext[idx], enc_buf, key_schedule, 256);
+ //printf("\nCiphertext : ");
+ //print_hex(ciphertext[idx], 16);
+ //printf("\n-decrypted to: ");
+ //print_hex(enc_buf, 16);
+ pass = pass && !memcmp(enc_buf, plaintext[idx], 16);
+
+ //printf("\n\n");
+ }
+
+ return(pass);
+}
+
+int aes_cbc_test()
+{
+ WORD key_schedule[60];
+ BYTE enc_buf[128];
+ BYTE plaintext[1][32] = {
+ {0x6b,0xc1,0xbe,0xe2,0x2e,0x40,0x9f,0x96,0xe9,0x3d,0x7e,0x11,0x73,0x93,0x17,0x2a,0xae,0x2d,0x8a,0x57,0x1e,0x03,0xac,0x9c,0x9e,0xb7,0x6f,0xac,0x45,0xaf,0x8e,0x51}
+ };
+ BYTE ciphertext[2][32] = {
+ {0xf5,0x8c,0x4c,0x04,0xd6,0xe5,0xf1,0xba,0x77,0x9e,0xab,0xfb,0x5f,0x7b,0xfb,0xd6,0x9c,0xfc,0x4e,0x96,0x7e,0xdb,0x80,0x8d,0x67,0x9f,0x77,0x7b,0xc6,0x70,0x2c,0x7d}
+ };
+ BYTE iv[1][16] = {
+ {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f}
+ };
+ BYTE key[1][32] = {
+ {0x60,0x3d,0xeb,0x10,0x15,0xca,0x71,0xbe,0x2b,0x73,0xae,0xf0,0x85,0x7d,0x77,0x81,0x1f,0x35,0x2c,0x07,0x3b,0x61,0x08,0xd7,0x2d,0x98,0x10,0xa3,0x09,0x14,0xdf,0xf4}
+ };
+ int pass = 1;
+
+ //printf("* CBC mode:\n");
+ aes_key_setup(key[0], key_schedule, 256);
+
+ //printf( "Key : ");
+ //print_hex(key[0], 32);
+ //printf("\nIV : ");
+ //print_hex(iv[0], 16);
+
+ aes_encrypt_cbc(plaintext[0], 32, enc_buf, key_schedule, 256, iv[0]);
+ //printf("\nPlaintext : ");
+ //print_hex(plaintext[0], 32);
+ //printf("\n-encrypted to: ");
+ //print_hex(enc_buf, 32);
+ //printf("\nCiphertext : ");
+ //print_hex(ciphertext[0], 32);
+ pass = pass && !memcmp(enc_buf, ciphertext[0], 32);
+
+ //printf("\n\n");
+ return(pass);
+}
+
+int aes_ctr_test()
+{
+ WORD key_schedule[60];
+ BYTE enc_buf[128];
+ BYTE plaintext[1][32] = {
+ {0x6b,0xc1,0xbe,0xe2,0x2e,0x40,0x9f,0x96,0xe9,0x3d,0x7e,0x11,0x73,0x93,0x17,0x2a,0xae,0x2d,0x8a,0x57,0x1e,0x03,0xac,0x9c,0x9e,0xb7,0x6f,0xac,0x45,0xaf,0x8e,0x51}
+ };
+ BYTE ciphertext[1][32] = {
+ {0x60,0x1e,0xc3,0x13,0x77,0x57,0x89,0xa5,0xb7,0xa7,0xf5,0x04,0xbb,0xf3,0xd2,0x28,0xf4,0x43,0xe3,0xca,0x4d,0x62,0xb5,0x9a,0xca,0x84,0xe9,0x90,0xca,0xca,0xf5,0xc5}
+ };
+ BYTE iv[1][16] = {
+ {0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff},
+ };
+ BYTE key[1][32] = {
+ {0x60,0x3d,0xeb,0x10,0x15,0xca,0x71,0xbe,0x2b,0x73,0xae,0xf0,0x85,0x7d,0x77,0x81,0x1f,0x35,0x2c,0x07,0x3b,0x61,0x08,0xd7,0x2d,0x98,0x10,0xa3,0x09,0x14,0xdf,0xf4}
+ };
+ int pass = 1;
+
+ //printf("* CTR mode:\n");
+ aes_key_setup(key[0], key_schedule, 256);
+
+ //printf( "Key : ");
+ //print_hex(key[0], 32);
+ //printf("\nIV : ");
+ //print_hex(iv[0], 16);
+
+ aes_encrypt_ctr(plaintext[0], 32, enc_buf, key_schedule, 256, iv[0]);
+ //printf("\nPlaintext : ");
+ //print_hex(plaintext[0], 32);
+ //printf("\n-encrypted to: ");
+ //print_hex(enc_buf, 32);
+ pass = pass && !memcmp(enc_buf, ciphertext[0], 32);
+
+ aes_decrypt_ctr(ciphertext[0], 32, enc_buf, key_schedule, 256, iv[0]);
+ //printf("\nCiphertext : ");
+ //print_hex(ciphertext[0], 32);
+ //printf("\n-decrypted to: ");
+ //print_hex(enc_buf, 32);
+ pass = pass && !memcmp(enc_buf, plaintext[0], 32);
+
+ //printf("\n\n");
+ return(pass);
+}
+
+int aes_ccm_test()
+{
+ int mac_auth;
+ WORD enc_buf_len;
+ BYTE enc_buf[128];
+ BYTE plaintext[3][32] = {
+ {0x20,0x21,0x22,0x23},
+ {0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f},
+ {0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37}
+ };
+ BYTE assoc[3][32] = {
+ {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07},
+ {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f},
+ {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13}
+ };
+ BYTE ciphertext[3][32 + 16] = {
+ {0x71,0x62,0x01,0x5b,0x4d,0xac,0x25,0x5d},
+ {0xd2,0xa1,0xf0,0xe0,0x51,0xea,0x5f,0x62,0x08,0x1a,0x77,0x92,0x07,0x3d,0x59,0x3d,0x1f,0xc6,0x4f,0xbf,0xac,0xcd},
+ {0xe3,0xb2,0x01,0xa9,0xf5,0xb7,0x1a,0x7a,0x9b,0x1c,0xea,0xec,0xcd,0x97,0xe7,0x0b,0x61,0x76,0xaa,0xd9,0xa4,0x42,0x8a,0xa5,0x48,0x43,0x92,0xfb,0xc1,0xb0,0x99,0x51}
+ };
+ BYTE iv[3][16] = {
+ {0x10,0x11,0x12,0x13,0x14,0x15,0x16},
+ {0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17},
+ {0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b}
+ };
+ BYTE key[1][32] = {
+ {0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f}
+ };
+ int pass = 1;
+
+ //printf("* CCM mode:\n");
+ //printf("Key : ");
+ //print_hex(key[0], 16);
+
+ //print_hex(plaintext[0], 4);
+ //print_hex(assoc[0], 8);
+ //print_hex(ciphertext[0], 8);
+ //print_hex(iv[0], 7);
+ //print_hex(key[0], 16);
+
+ aes_encrypt_ccm(plaintext[0], 4, assoc[0], 8, iv[0], 7, enc_buf, &enc_buf_len, 4, key[0], 128);
+ //printf("\nNONCE : ");
+ //print_hex(iv[0], 7);
+ //printf("\nAssoc. Data : ");
+ //print_hex(assoc[0], 8);
+ //printf("\nPayload : ");
+ //print_hex(plaintext[0], 4);
+ //printf("\n-encrypted to: ");
+ //print_hex(enc_buf, enc_buf_len);
+ pass = pass && !memcmp(enc_buf, ciphertext[0], enc_buf_len);
+
+ aes_decrypt_ccm(ciphertext[0], 8, assoc[0], 8, iv[0], 7, enc_buf, &enc_buf_len, 4, &mac_auth, key[0], 128);
+ //printf("\n-Ciphertext : ");
+ //print_hex(ciphertext[0], 8);
+ //printf("\n-decrypted to: ");
+ //print_hex(enc_buf, enc_buf_len);
+ //printf("\nAuthenticated: %d ", mac_auth);
+ pass = pass && !memcmp(enc_buf, plaintext[0], enc_buf_len) && mac_auth;
+
+
+ aes_encrypt_ccm(plaintext[1], 16, assoc[1], 16, iv[1], 8, enc_buf, &enc_buf_len, 6, key[0], 128);
+ //printf("\n\nNONCE : ");
+ //print_hex(iv[1], 8);
+ //printf("\nAssoc. Data : ");
+ //print_hex(assoc[1], 16);
+ //printf("\nPayload : ");
+ //print_hex(plaintext[1], 16);
+ //printf("\n-encrypted to: ");
+ //print_hex(enc_buf, enc_buf_len);
+ pass = pass && !memcmp(enc_buf, ciphertext[1], enc_buf_len);
+
+ aes_decrypt_ccm(ciphertext[1], 22, assoc[1], 16, iv[1], 8, enc_buf, &enc_buf_len, 6, &mac_auth, key[0], 128);
+ //printf("\n-Ciphertext : ");
+ //print_hex(ciphertext[1], 22);
+ //printf("\n-decrypted to: ");
+ //print_hex(enc_buf, enc_buf_len);
+ //printf("\nAuthenticated: %d ", mac_auth);
+ pass = pass && !memcmp(enc_buf, plaintext[1], enc_buf_len) && mac_auth;
+
+
+ aes_encrypt_ccm(plaintext[2], 24, assoc[2], 20, iv[2], 12, enc_buf, &enc_buf_len, 8, key[0], 128);
+ //printf("\n\nNONCE : ");
+ //print_hex(iv[2], 12);
+ //printf("\nAssoc. Data : ");
+ //print_hex(assoc[2], 20);
+ //printf("\nPayload : ");
+ //print_hex(plaintext[2], 24);
+ //printf("\n-encrypted to: ");
+ //print_hex(enc_buf, enc_buf_len);
+ pass = pass && !memcmp(enc_buf, ciphertext[2], enc_buf_len);
+
+ aes_decrypt_ccm(ciphertext[2], 32, assoc[2], 20, iv[2], 12, enc_buf, &enc_buf_len, 8, &mac_auth, key[0], 128);
+ //printf("\n-Ciphertext : ");
+ //print_hex(ciphertext[2], 32);
+ //printf("\n-decrypted to: ");
+ //print_hex(enc_buf, enc_buf_len);
+ //printf("\nAuthenticated: %d ", mac_auth);
+ pass = pass && !memcmp(enc_buf, plaintext[2], enc_buf_len) && mac_auth;
+
+ //printf("\n\n");
+ return(pass);
+}
+
+int aes_test()
+{
+ int pass = 1;
+
+ pass = pass && aes_ecb_test();
+ pass = pass && aes_cbc_test();
+ pass = pass && aes_ctr_test();
+ pass = pass && aes_ccm_test();
+
+ return(pass);
+}
+
+int main(int argc, char *argv[])
+{
+ printf("AES Tests: %s\n", aes_test() ? "SUCCEEDED" : "FAILED");
+
+ return(0);
+}
--- /dev/null
+/*********************************************************************
+* Filename: arcfour.c
+* Author: Brad Conte (brad AT bradconte.com)
+* Copyright:
+* Disclaimer: This code is presented "as is" without any guarantees.
+* Details: Implementation of the ARCFOUR encryption algorithm.
+ Algorithm specification can be found here:
+ * http://en.wikipedia.org/wiki/RC4
+*********************************************************************/
+
+/*************************** HEADER FILES ***************************/
+#include <stdlib.h>
+#include "arcfour.h"
+
+/*********************** FUNCTION DEFINITIONS ***********************/
+void arcfour_key_setup(BYTE state[], const BYTE key[], int len)
+{
+ int i, j;
+ BYTE t;
+
+ for (i = 0; i < 256; ++i)
+ state[i] = i;
+ for (i = 0, j = 0; i < 256; ++i) {
+ j = (j + state[i] + key[i % len]) % 256;
+ t = state[i];
+ state[i] = state[j];
+ state[j] = t;
+ }
+}
+
+void arcfour_generate_stream(BYTE state[], BYTE out[], size_t len)
+{
+ int i, j;
+ size_t idx;
+ BYTE t;
+
+ for (idx = 0, i = 0, j = 0; idx < len; ++idx) {
+ i = (i + 1) % 256;
+ j = (j + state[i]) % 256;
+ t = state[i];
+ state[i] = state[j];
+ state[j] = t;
+ out[idx] = state[(state[i] + state[j]) % 256];
+ }
+}
--- /dev/null
+/*********************************************************************
+* Filename: arcfour.h
+* Author: Brad Conte (brad AT bradconte.com)
+* Copyright:
+* Disclaimer: This code is presented "as is" without any guarantees.
+* Details: Defines the API for the corresponding ARCFOUR implementation.
+*********************************************************************/
+
+#ifndef ARCFOUR_H
+#define ARCFOUR_H
+
+/*************************** HEADER FILES ***************************/
+#include <stddef.h>
+
+/**************************** DATA TYPES ****************************/
+typedef unsigned char BYTE; // 8-bit byte
+
+/*********************** FUNCTION DECLARATIONS **********************/
+// Input: state - the state used to generate the keystream
+// key - Key to use to initialize the state
+// len - length of key in bytes (valid lenth is 1 to 256)
+void arcfour_key_setup(BYTE state[], const BYTE key[], int len);
+
+// Pseudo-Random Generator Algorithm
+// Input: state - the state used to generate the keystream
+// out - Must be allocated to be of at least "len" length
+// len - number of bytes to generate
+void arcfour_generate_stream(BYTE state[], BYTE out[], size_t len);
+
+#endif // ARCFOUR_H
--- /dev/null
+/*********************************************************************
+* Filename: arcfour_test.c
+* Author: Brad Conte (brad AT bradconte.com)
+* Copyright:
+* Disclaimer: This code is presented "as is" without any guarantees.
+* Details: Performs known-answer tests on the corresponding ARCFOUR
+ implementation. These tests do not encompass the full
+ range of available test vectors, however, if the tests
+ pass it is very, very likely that the code is correct
+ and was compiled properly. This code also serves as
+ example usage of the functions.
+*********************************************************************/
+
+/*************************** HEADER FILES ***************************/
+#include <stdio.h>
+#include <memory.h>
+#include "arcfour.h"
+
+/*********************** FUNCTION DEFINITIONS ***********************/
+int rc4_test()
+{
+ BYTE state[256];
+ BYTE key[3][10] = {{"Key"}, {"Wiki"}, {"Secret"}};
+ BYTE stream[3][10] = {{0xEB,0x9F,0x77,0x81,0xB7,0x34,0xCA,0x72,0xA7,0x19},
+ {0x60,0x44,0xdb,0x6d,0x41,0xb7},
+ {0x04,0xd4,0x6b,0x05,0x3c,0xa8,0x7b,0x59}};
+ int stream_len[3] = {10,6,8};
+ BYTE buf[1024];
+ int idx;
+ int pass = 1;
+
+ // Only test the output stream. Note that the state can be reused.
+ for (idx = 0; idx < 3; idx++) {
+ arcfour_key_setup(state, key[idx], strlen(key[idx]));
+ arcfour_generate_stream(state, buf, stream_len[idx]);
+ pass = pass && !memcmp(stream[idx], buf, stream_len[idx]);
+ }
+
+ return(pass);
+}
+
+int main()
+{
+ printf("ARCFOUR tests: %s\n", rc4_test() ? "SUCCEEDED" : "FAILED");
+
+ return(0);
+}
--- /dev/null
+/*********************************************************************
+* Filename: base64.c
+* Author: Brad Conte (brad AT bradconte.com)
+* Copyright:
+* Disclaimer: This code is presented "as is" without any guarantees.
+* Details: Implementation of the Base64 encoding algorithm.
+*********************************************************************/
+
+/*************************** HEADER FILES ***************************/
+#include <stdlib.h>
+#include "base64.h"
+
+/****************************** MACROS ******************************/
+#define NEWLINE_INVL 76
+
+/**************************** VARIABLES *****************************/
+// Note: To change the charset to a URL encoding, replace the '+' and '/' with '*' and '-'
+static const BYTE charset[]={"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"};
+
+/*********************** FUNCTION DEFINITIONS ***********************/
+BYTE revchar(char ch)
+{
+ if (ch >= 'A' && ch <= 'Z')
+ ch -= 'A';
+ else if (ch >= 'a' && ch <='z')
+ ch = ch - 'a' + 26;
+ else if (ch >= '0' && ch <='9')
+ ch = ch - '0' + 52;
+ else if (ch == '+')
+ ch = 62;
+ else if (ch == '/')
+ ch = 63;
+
+ return(ch);
+}
+
+size_t base64_encode(const BYTE in[], BYTE out[], size_t len, int newline_flag)
+{
+ size_t idx, idx2, blks, blk_ceiling, left_over, newline_count = 0;
+
+ blks = (len / 3);
+ left_over = len % 3;
+
+ if (out == NULL) {
+ idx2 = blks * 4 ;
+ if (left_over)
+ idx2 += 4;
+ if (newline_flag)
+ idx2 += len / 57; // (NEWLINE_INVL / 4) * 3 = 57. One newline per 57 input bytes.
+ }
+ else {
+ // Since 3 input bytes = 4 output bytes, determine out how many even sets of
+ // 3 bytes the input has.
+ blk_ceiling = blks * 3;
+ for (idx = 0, idx2 = 0; idx < blk_ceiling; idx += 3, idx2 += 4) {
+ out[idx2] = charset[in[idx] >> 2];
+ out[idx2 + 1] = charset[((in[idx] & 0x03) << 4) | (in[idx + 1] >> 4)];
+ out[idx2 + 2] = charset[((in[idx + 1] & 0x0f) << 2) | (in[idx + 2] >> 6)];
+ out[idx2 + 3] = charset[in[idx + 2] & 0x3F];
+ // The offical standard requires a newline every 76 characters.
+ // (Eg, first newline is character 77 of the output.)
+ if (((idx2 - newline_count + 4) % NEWLINE_INVL == 0) && newline_flag) {
+ out[idx2 + 4] = '\n';
+ idx2++;
+ newline_count++;
+ }
+ }
+
+ if (left_over == 1) {
+ out[idx2] = charset[in[idx] >> 2];
+ out[idx2 + 1] = charset[(in[idx] & 0x03) << 4];
+ out[idx2 + 2] = '=';
+ out[idx2 + 3] = '=';
+ idx2 += 4;
+ }
+ else if (left_over == 2) {
+ out[idx2] = charset[in[idx] >> 2];
+ out[idx2 + 1] = charset[((in[idx] & 0x03) << 4) | (in[idx + 1] >> 4)];
+ out[idx2 + 2] = charset[(in[idx + 1] & 0x0F) << 2];
+ out[idx2 + 3] = '=';
+ idx2 += 4;
+ }
+ }
+
+ return(idx2);
+}
+
+size_t base64_decode(const BYTE in[], BYTE out[], size_t len)
+{
+ BYTE ch;
+ size_t idx, idx2, blks, blk_ceiling, left_over;
+
+ if (in[len - 1] == '=')
+ len--;
+ if (in[len - 1] == '=')
+ len--;
+
+ blks = len / 4;
+ left_over = len % 4;
+
+ if (out == NULL) {
+ if (len >= 77 && in[NEWLINE_INVL] == '\n') // Verify that newlines where used.
+ len -= len / (NEWLINE_INVL + 1);
+ blks = len / 4;
+ left_over = len % 4;
+
+ idx = blks * 3;
+ if (left_over == 2)
+ idx ++;
+ else if (left_over == 3)
+ idx += 2;
+ }
+ else {
+ blk_ceiling = blks * 4;
+ for (idx = 0, idx2 = 0; idx2 < blk_ceiling; idx += 3, idx2 += 4) {
+ if (in[idx2] == '\n')
+ idx2++;
+ out[idx] = (revchar(in[idx2]) << 2) | ((revchar(in[idx2 + 1]) & 0x30) >> 4);
+ out[idx + 1] = (revchar(in[idx2 + 1]) << 4) | (revchar(in[idx2 + 2]) >> 2);
+ out[idx + 2] = (revchar(in[idx2 + 2]) << 6) | revchar(in[idx2 + 3]);
+ }
+
+ if (left_over == 2) {
+ out[idx] = (revchar(in[idx2]) << 2) | ((revchar(in[idx2 + 1]) & 0x30) >> 4);
+ idx++;
+ }
+ else if (left_over == 3) {
+ out[idx] = (revchar(in[idx2]) << 2) | ((revchar(in[idx2 + 1]) & 0x30) >> 4);
+ out[idx + 1] = (revchar(in[idx2 + 1]) << 4) | (revchar(in[idx2 + 2]) >> 2);
+ idx += 2;
+ }
+ }
+
+ return(idx);
+}
--- /dev/null
+/*********************************************************************
+* Filename: base64.h
+* Author: Brad Conte (brad AT bradconte.com)
+* Copyright:
+* Disclaimer: This code is presented "as is" without any guarantees.
+* Details: Defines the API for the corresponding Base64 implementation.
+*********************************************************************/
+
+#ifndef BASE64_H
+#define BASE64_H
+
+/*************************** HEADER FILES ***************************/
+#include <stddef.h>
+
+/**************************** DATA TYPES ****************************/
+typedef unsigned char BYTE; // 8-bit byte
+
+/*********************** FUNCTION DECLARATIONS **********************/
+// Returns the size of the output. If called with out = NULL, will just return
+// the size of what the output would have been (without a terminating NULL).
+size_t base64_encode(const BYTE in[], BYTE out[], size_t len, int newline_flag);
+
+// Returns the size of the output. If called with out = NULL, will just return
+// the size of what the output would have been (without a terminating NULL).
+size_t base64_decode(const BYTE in[], BYTE out[], size_t len);
+
+#endif // BASE64_H
--- /dev/null
+/*********************************************************************
+* Filename: blowfish_test.c
+* Author: Brad Conte (brad AT bradconte.com)
+* Copyright:
+* Disclaimer: This code is presented "as is" without any guarantees.
+* Details: Performs known-answer tests on the corresponding Base64
+ implementation. These tests do not encompass the full
+ range of available test vectors, however, if the tests
+ pass it is very, very likely that the code is correct
+ and was compiled properly. This code also serves as
+ example usage of the functions.
+*********************************************************************/
+
+/*************************** HEADER FILES ***************************/
+#include <stdio.h>
+#include <memory.h>
+#include "base64.h"
+
+/*********************** FUNCTION DEFINITIONS ***********************/
+int base64_test()
+{
+ BYTE text[3][1024] = {{"fo"},
+ {"foobar"},
+ {"Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure."}};
+ BYTE code[3][1024] = {{"Zm8="},
+ {"Zm9vYmFy"},
+ {"TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz\nIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg\ndGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu\ndWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo\nZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4="}};
+ BYTE buf[1024];
+ size_t buf_len;
+ int pass = 1;
+ int idx;
+
+ for (idx = 0; idx < 3; idx++) {
+ buf_len = base64_encode(text[idx], buf, strlen(text[idx]), 1);
+ pass = pass && ((buf_len == strlen(code[idx])) &&
+ (buf_len == base64_encode(text[idx], NULL, strlen(text[idx]), 1)));
+ pass = pass && !strcmp(code[idx], buf);
+
+ memset(buf, 0, sizeof(buf));
+ buf_len = base64_decode(code[idx], buf, strlen(code[idx]));
+ pass = pass && ((buf_len == strlen(text[idx])) &&
+ (buf_len == base64_decode(code[idx], NULL, strlen(code[idx]))));
+ pass = pass && !strcmp(text[idx], buf);
+ }
+
+ return(pass);
+}
+
+int main()
+{
+ printf("Base64 tests: %s\n", base64_test() ? "PASSED" : "FAILED");
+
+ return 0;
+}
--- /dev/null
+/*********************************************************************
+* Filename: blowfish.c
+* Author: Brad Conte (brad AT bradconte.com)
+* Copyright:
+* Disclaimer: This code is presented "as is" without any guarantees.
+* Details: Implementation of the Blowfish encryption algorithm.
+ Modes of operation (such as CBC) are not included.
+ Algorithm specification can be found here:
+ * http://www.schneier.com/blowfish.html
+*********************************************************************/
+
+/*************************** HEADER FILES ***************************/
+#include <stdlib.h>
+#include <memory.h>
+#include "blowfish.h"
+
+/****************************** MACROS ******************************/
+#define F(x,t) t = keystruct->s[0][(x) >> 24]; \
+ t += keystruct->s[1][((x) >> 16) & 0xff]; \
+ t ^= keystruct->s[2][((x) >> 8) & 0xff]; \
+ t += keystruct->s[3][(x) & 0xff];
+#define swap(r,l,t) t = l; l = r; r = t;
+#define ITERATION(l,r,t,pval) l ^= keystruct->p[pval]; F(l,t); r^= t; swap(r,l,t);
+
+/**************************** VARIABLES *****************************/
+static const WORD p_perm[18] = {
+ 0x243F6A88,0x85A308D3,0x13198A2E,0x03707344,0xA4093822,0x299F31D0,0x082EFA98,
+ 0xEC4E6C89,0x452821E6,0x38D01377,0xBE5466CF,0x34E90C6C,0xC0AC29B7,0xC97C50DD,
+ 0x3F84D5B5,0xB5470917,0x9216D5D9,0x8979FB1B
+};
+
+static const WORD s_perm[4][256] = { {
+ 0xD1310BA6,0x98DFB5AC,0x2FFD72DB,0xD01ADFB7,0xB8E1AFED,0x6A267E96,0xBA7C9045,0xF12C7F99,
+ 0x24A19947,0xB3916CF7,0x0801F2E2,0x858EFC16,0x636920D8,0x71574E69,0xA458FEA3,0xF4933D7E,
+ 0x0D95748F,0x728EB658,0x718BCD58,0x82154AEE,0x7B54A41D,0xC25A59B5,0x9C30D539,0x2AF26013,
+ 0xC5D1B023,0x286085F0,0xCA417918,0xB8DB38EF,0x8E79DCB0,0x603A180E,0x6C9E0E8B,0xB01E8A3E,
+ 0xD71577C1,0xBD314B27,0x78AF2FDA,0x55605C60,0xE65525F3,0xAA55AB94,0x57489862,0x63E81440,
+ 0x55CA396A,0x2AAB10B6,0xB4CC5C34,0x1141E8CE,0xA15486AF,0x7C72E993,0xB3EE1411,0x636FBC2A,
+ 0x2BA9C55D,0x741831F6,0xCE5C3E16,0x9B87931E,0xAFD6BA33,0x6C24CF5C,0x7A325381,0x28958677,
+ 0x3B8F4898,0x6B4BB9AF,0xC4BFE81B,0x66282193,0x61D809CC,0xFB21A991,0x487CAC60,0x5DEC8032,
+ 0xEF845D5D,0xE98575B1,0xDC262302,0xEB651B88,0x23893E81,0xD396ACC5,0x0F6D6FF3,0x83F44239,
+ 0x2E0B4482,0xA4842004,0x69C8F04A,0x9E1F9B5E,0x21C66842,0xF6E96C9A,0x670C9C61,0xABD388F0,
+ 0x6A51A0D2,0xD8542F68,0x960FA728,0xAB5133A3,0x6EEF0B6C,0x137A3BE4,0xBA3BF050,0x7EFB2A98,
+ 0xA1F1651D,0x39AF0176,0x66CA593E,0x82430E88,0x8CEE8619,0x456F9FB4,0x7D84A5C3,0x3B8B5EBE,
+ 0xE06F75D8,0x85C12073,0x401A449F,0x56C16AA6,0x4ED3AA62,0x363F7706,0x1BFEDF72,0x429B023D,
+ 0x37D0D724,0xD00A1248,0xDB0FEAD3,0x49F1C09B,0x075372C9,0x80991B7B,0x25D479D8,0xF6E8DEF7,
+ 0xE3FE501A,0xB6794C3B,0x976CE0BD,0x04C006BA,0xC1A94FB6,0x409F60C4,0x5E5C9EC2,0x196A2463,
+ 0x68FB6FAF,0x3E6C53B5,0x1339B2EB,0x3B52EC6F,0x6DFC511F,0x9B30952C,0xCC814544,0xAF5EBD09,
+ 0xBEE3D004,0xDE334AFD,0x660F2807,0x192E4BB3,0xC0CBA857,0x45C8740F,0xD20B5F39,0xB9D3FBDB,
+ 0x5579C0BD,0x1A60320A,0xD6A100C6,0x402C7279,0x679F25FE,0xFB1FA3CC,0x8EA5E9F8,0xDB3222F8,
+ 0x3C7516DF,0xFD616B15,0x2F501EC8,0xAD0552AB,0x323DB5FA,0xFD238760,0x53317B48,0x3E00DF82,
+ 0x9E5C57BB,0xCA6F8CA0,0x1A87562E,0xDF1769DB,0xD542A8F6,0x287EFFC3,0xAC6732C6,0x8C4F5573,
+ 0x695B27B0,0xBBCA58C8,0xE1FFA35D,0xB8F011A0,0x10FA3D98,0xFD2183B8,0x4AFCB56C,0x2DD1D35B,
+ 0x9A53E479,0xB6F84565,0xD28E49BC,0x4BFB9790,0xE1DDF2DA,0xA4CB7E33,0x62FB1341,0xCEE4C6E8,
+ 0xEF20CADA,0x36774C01,0xD07E9EFE,0x2BF11FB4,0x95DBDA4D,0xAE909198,0xEAAD8E71,0x6B93D5A0,
+ 0xD08ED1D0,0xAFC725E0,0x8E3C5B2F,0x8E7594B7,0x8FF6E2FB,0xF2122B64,0x8888B812,0x900DF01C,
+ 0x4FAD5EA0,0x688FC31C,0xD1CFF191,0xB3A8C1AD,0x2F2F2218,0xBE0E1777,0xEA752DFE,0x8B021FA1,
+ 0xE5A0CC0F,0xB56F74E8,0x18ACF3D6,0xCE89E299,0xB4A84FE0,0xFD13E0B7,0x7CC43B81,0xD2ADA8D9,
+ 0x165FA266,0x80957705,0x93CC7314,0x211A1477,0xE6AD2065,0x77B5FA86,0xC75442F5,0xFB9D35CF,
+ 0xEBCDAF0C,0x7B3E89A0,0xD6411BD3,0xAE1E7E49,0x00250E2D,0x2071B35E,0x226800BB,0x57B8E0AF,
+ 0x2464369B,0xF009B91E,0x5563911D,0x59DFA6AA,0x78C14389,0xD95A537F,0x207D5BA2,0x02E5B9C5,
+ 0x83260376,0x6295CFA9,0x11C81968,0x4E734A41,0xB3472DCA,0x7B14A94A,0x1B510052,0x9A532915,
+ 0xD60F573F,0xBC9BC6E4,0x2B60A476,0x81E67400,0x08BA6FB5,0x571BE91F,0xF296EC6B,0x2A0DD915,
+ 0xB6636521,0xE7B9F9B6,0xFF34052E,0xC5855664,0x53B02D5D,0xA99F8FA1,0x08BA4799,0x6E85076A
+},{
+ 0x4B7A70E9,0xB5B32944,0xDB75092E,0xC4192623,0xAD6EA6B0,0x49A7DF7D,0x9CEE60B8,0x8FEDB266,
+ 0xECAA8C71,0x699A17FF,0x5664526C,0xC2B19EE1,0x193602A5,0x75094C29,0xA0591340,0xE4183A3E,
+ 0x3F54989A,0x5B429D65,0x6B8FE4D6,0x99F73FD6,0xA1D29C07,0xEFE830F5,0x4D2D38E6,0xF0255DC1,
+ 0x4CDD2086,0x8470EB26,0x6382E9C6,0x021ECC5E,0x09686B3F,0x3EBAEFC9,0x3C971814,0x6B6A70A1,
+ 0x687F3584,0x52A0E286,0xB79C5305,0xAA500737,0x3E07841C,0x7FDEAE5C,0x8E7D44EC,0x5716F2B8,
+ 0xB03ADA37,0xF0500C0D,0xF01C1F04,0x0200B3FF,0xAE0CF51A,0x3CB574B2,0x25837A58,0xDC0921BD,
+ 0xD19113F9,0x7CA92FF6,0x94324773,0x22F54701,0x3AE5E581,0x37C2DADC,0xC8B57634,0x9AF3DDA7,
+ 0xA9446146,0x0FD0030E,0xECC8C73E,0xA4751E41,0xE238CD99,0x3BEA0E2F,0x3280BBA1,0x183EB331,
+ 0x4E548B38,0x4F6DB908,0x6F420D03,0xF60A04BF,0x2CB81290,0x24977C79,0x5679B072,0xBCAF89AF,
+ 0xDE9A771F,0xD9930810,0xB38BAE12,0xDCCF3F2E,0x5512721F,0x2E6B7124,0x501ADDE6,0x9F84CD87,
+ 0x7A584718,0x7408DA17,0xBC9F9ABC,0xE94B7D8C,0xEC7AEC3A,0xDB851DFA,0x63094366,0xC464C3D2,
+ 0xEF1C1847,0x3215D908,0xDD433B37,0x24C2BA16,0x12A14D43,0x2A65C451,0x50940002,0x133AE4DD,
+ 0x71DFF89E,0x10314E55,0x81AC77D6,0x5F11199B,0x043556F1,0xD7A3C76B,0x3C11183B,0x5924A509,
+ 0xF28FE6ED,0x97F1FBFA,0x9EBABF2C,0x1E153C6E,0x86E34570,0xEAE96FB1,0x860E5E0A,0x5A3E2AB3,
+ 0x771FE71C,0x4E3D06FA,0x2965DCB9,0x99E71D0F,0x803E89D6,0x5266C825,0x2E4CC978,0x9C10B36A,
+ 0xC6150EBA,0x94E2EA78,0xA5FC3C53,0x1E0A2DF4,0xF2F74EA7,0x361D2B3D,0x1939260F,0x19C27960,
+ 0x5223A708,0xF71312B6,0xEBADFE6E,0xEAC31F66,0xE3BC4595,0xA67BC883,0xB17F37D1,0x018CFF28,
+ 0xC332DDEF,0xBE6C5AA5,0x65582185,0x68AB9802,0xEECEA50F,0xDB2F953B,0x2AEF7DAD,0x5B6E2F84,
+ 0x1521B628,0x29076170,0xECDD4775,0x619F1510,0x13CCA830,0xEB61BD96,0x0334FE1E,0xAA0363CF,
+ 0xB5735C90,0x4C70A239,0xD59E9E0B,0xCBAADE14,0xEECC86BC,0x60622CA7,0x9CAB5CAB,0xB2F3846E,
+ 0x648B1EAF,0x19BDF0CA,0xA02369B9,0x655ABB50,0x40685A32,0x3C2AB4B3,0x319EE9D5,0xC021B8F7,
+ 0x9B540B19,0x875FA099,0x95F7997E,0x623D7DA8,0xF837889A,0x97E32D77,0x11ED935F,0x16681281,
+ 0x0E358829,0xC7E61FD6,0x96DEDFA1,0x7858BA99,0x57F584A5,0x1B227263,0x9B83C3FF,0x1AC24696,
+ 0xCDB30AEB,0x532E3054,0x8FD948E4,0x6DBC3128,0x58EBF2EF,0x34C6FFEA,0xFE28ED61,0xEE7C3C73,
+ 0x5D4A14D9,0xE864B7E3,0x42105D14,0x203E13E0,0x45EEE2B6,0xA3AAABEA,0xDB6C4F15,0xFACB4FD0,
+ 0xC742F442,0xEF6ABBB5,0x654F3B1D,0x41CD2105,0xD81E799E,0x86854DC7,0xE44B476A,0x3D816250,
+ 0xCF62A1F2,0x5B8D2646,0xFC8883A0,0xC1C7B6A3,0x7F1524C3,0x69CB7492,0x47848A0B,0x5692B285,
+ 0x095BBF00,0xAD19489D,0x1462B174,0x23820E00,0x58428D2A,0x0C55F5EA,0x1DADF43E,0x233F7061,
+ 0x3372F092,0x8D937E41,0xD65FECF1,0x6C223BDB,0x7CDE3759,0xCBEE7460,0x4085F2A7,0xCE77326E,
+ 0xA6078084,0x19F8509E,0xE8EFD855,0x61D99735,0xA969A7AA,0xC50C06C2,0x5A04ABFC,0x800BCADC,
+ 0x9E447A2E,0xC3453484,0xFDD56705,0x0E1E9EC9,0xDB73DBD3,0x105588CD,0x675FDA79,0xE3674340,
+ 0xC5C43465,0x713E38D8,0x3D28F89E,0xF16DFF20,0x153E21E7,0x8FB03D4A,0xE6E39F2B,0xDB83ADF7
+},{
+ 0xE93D5A68,0x948140F7,0xF64C261C,0x94692934,0x411520F7,0x7602D4F7,0xBCF46B2E,0xD4A20068,
+ 0xD4082471,0x3320F46A,0x43B7D4B7,0x500061AF,0x1E39F62E,0x97244546,0x14214F74,0xBF8B8840,
+ 0x4D95FC1D,0x96B591AF,0x70F4DDD3,0x66A02F45,0xBFBC09EC,0x03BD9785,0x7FAC6DD0,0x31CB8504,
+ 0x96EB27B3,0x55FD3941,0xDA2547E6,0xABCA0A9A,0x28507825,0x530429F4,0x0A2C86DA,0xE9B66DFB,
+ 0x68DC1462,0xD7486900,0x680EC0A4,0x27A18DEE,0x4F3FFEA2,0xE887AD8C,0xB58CE006,0x7AF4D6B6,
+ 0xAACE1E7C,0xD3375FEC,0xCE78A399,0x406B2A42,0x20FE9E35,0xD9F385B9,0xEE39D7AB,0x3B124E8B,
+ 0x1DC9FAF7,0x4B6D1856,0x26A36631,0xEAE397B2,0x3A6EFA74,0xDD5B4332,0x6841E7F7,0xCA7820FB,
+ 0xFB0AF54E,0xD8FEB397,0x454056AC,0xBA489527,0x55533A3A,0x20838D87,0xFE6BA9B7,0xD096954B,
+ 0x55A867BC,0xA1159A58,0xCCA92963,0x99E1DB33,0xA62A4A56,0x3F3125F9,0x5EF47E1C,0x9029317C,
+ 0xFDF8E802,0x04272F70,0x80BB155C,0x05282CE3,0x95C11548,0xE4C66D22,0x48C1133F,0xC70F86DC,
+ 0x07F9C9EE,0x41041F0F,0x404779A4,0x5D886E17,0x325F51EB,0xD59BC0D1,0xF2BCC18F,0x41113564,
+ 0x257B7834,0x602A9C60,0xDFF8E8A3,0x1F636C1B,0x0E12B4C2,0x02E1329E,0xAF664FD1,0xCAD18115,
+ 0x6B2395E0,0x333E92E1,0x3B240B62,0xEEBEB922,0x85B2A20E,0xE6BA0D99,0xDE720C8C,0x2DA2F728,
+ 0xD0127845,0x95B794FD,0x647D0862,0xE7CCF5F0,0x5449A36F,0x877D48FA,0xC39DFD27,0xF33E8D1E,
+ 0x0A476341,0x992EFF74,0x3A6F6EAB,0xF4F8FD37,0xA812DC60,0xA1EBDDF8,0x991BE14C,0xDB6E6B0D,
+ 0xC67B5510,0x6D672C37,0x2765D43B,0xDCD0E804,0xF1290DC7,0xCC00FFA3,0xB5390F92,0x690FED0B,
+ 0x667B9FFB,0xCEDB7D9C,0xA091CF0B,0xD9155EA3,0xBB132F88,0x515BAD24,0x7B9479BF,0x763BD6EB,
+ 0x37392EB3,0xCC115979,0x8026E297,0xF42E312D,0x6842ADA7,0xC66A2B3B,0x12754CCC,0x782EF11C,
+ 0x6A124237,0xB79251E7,0x06A1BBE6,0x4BFB6350,0x1A6B1018,0x11CAEDFA,0x3D25BDD8,0xE2E1C3C9,
+ 0x44421659,0x0A121386,0xD90CEC6E,0xD5ABEA2A,0x64AF674E,0xDA86A85F,0xBEBFE988,0x64E4C3FE,
+ 0x9DBC8057,0xF0F7C086,0x60787BF8,0x6003604D,0xD1FD8346,0xF6381FB0,0x7745AE04,0xD736FCCC,
+ 0x83426B33,0xF01EAB71,0xB0804187,0x3C005E5F,0x77A057BE,0xBDE8AE24,0x55464299,0xBF582E61,
+ 0x4E58F48F,0xF2DDFDA2,0xF474EF38,0x8789BDC2,0x5366F9C3,0xC8B38E74,0xB475F255,0x46FCD9B9,
+ 0x7AEB2661,0x8B1DDF84,0x846A0E79,0x915F95E2,0x466E598E,0x20B45770,0x8CD55591,0xC902DE4C,
+ 0xB90BACE1,0xBB8205D0,0x11A86248,0x7574A99E,0xB77F19B6,0xE0A9DC09,0x662D09A1,0xC4324633,
+ 0xE85A1F02,0x09F0BE8C,0x4A99A025,0x1D6EFE10,0x1AB93D1D,0x0BA5A4DF,0xA186F20F,0x2868F169,
+ 0xDCB7DA83,0x573906FE,0xA1E2CE9B,0x4FCD7F52,0x50115E01,0xA70683FA,0xA002B5C4,0x0DE6D027,
+ 0x9AF88C27,0x773F8641,0xC3604C06,0x61A806B5,0xF0177A28,0xC0F586E0,0x006058AA,0x30DC7D62,
+ 0x11E69ED7,0x2338EA63,0x53C2DD94,0xC2C21634,0xBBCBEE56,0x90BCB6DE,0xEBFC7DA1,0xCE591D76,
+ 0x6F05E409,0x4B7C0188,0x39720A3D,0x7C927C24,0x86E3725F,0x724D9DB9,0x1AC15BB4,0xD39EB8FC,
+ 0xED545578,0x08FCA5B5,0xD83D7CD3,0x4DAD0FC4,0x1E50EF5E,0xB161E6F8,0xA28514D9,0x6C51133C,
+ 0x6FD5C7E7,0x56E14EC4,0x362ABFCE,0xDDC6C837,0xD79A3234,0x92638212,0x670EFA8E,0x406000E0
+},{
+ 0x3A39CE37,0xD3FAF5CF,0xABC27737,0x5AC52D1B,0x5CB0679E,0x4FA33742,0xD3822740,0x99BC9BBE,
+ 0xD5118E9D,0xBF0F7315,0xD62D1C7E,0xC700C47B,0xB78C1B6B,0x21A19045,0xB26EB1BE,0x6A366EB4,
+ 0x5748AB2F,0xBC946E79,0xC6A376D2,0x6549C2C8,0x530FF8EE,0x468DDE7D,0xD5730A1D,0x4CD04DC6,
+ 0x2939BBDB,0xA9BA4650,0xAC9526E8,0xBE5EE304,0xA1FAD5F0,0x6A2D519A,0x63EF8CE2,0x9A86EE22,
+ 0xC089C2B8,0x43242EF6,0xA51E03AA,0x9CF2D0A4,0x83C061BA,0x9BE96A4D,0x8FE51550,0xBA645BD6,
+ 0x2826A2F9,0xA73A3AE1,0x4BA99586,0xEF5562E9,0xC72FEFD3,0xF752F7DA,0x3F046F69,0x77FA0A59,
+ 0x80E4A915,0x87B08601,0x9B09E6AD,0x3B3EE593,0xE990FD5A,0x9E34D797,0x2CF0B7D9,0x022B8B51,
+ 0x96D5AC3A,0x017DA67D,0xD1CF3ED6,0x7C7D2D28,0x1F9F25CF,0xADF2B89B,0x5AD6B472,0x5A88F54C,
+ 0xE029AC71,0xE019A5E6,0x47B0ACFD,0xED93FA9B,0xE8D3C48D,0x283B57CC,0xF8D56629,0x79132E28,
+ 0x785F0191,0xED756055,0xF7960E44,0xE3D35E8C,0x15056DD4,0x88F46DBA,0x03A16125,0x0564F0BD,
+ 0xC3EB9E15,0x3C9057A2,0x97271AEC,0xA93A072A,0x1B3F6D9B,0x1E6321F5,0xF59C66FB,0x26DCF319,
+ 0x7533D928,0xB155FDF5,0x03563482,0x8ABA3CBB,0x28517711,0xC20AD9F8,0xABCC5167,0xCCAD925F,
+ 0x4DE81751,0x3830DC8E,0x379D5862,0x9320F991,0xEA7A90C2,0xFB3E7BCE,0x5121CE64,0x774FBE32,
+ 0xA8B6E37E,0xC3293D46,0x48DE5369,0x6413E680,0xA2AE0810,0xDD6DB224,0x69852DFD,0x09072166,
+ 0xB39A460A,0x6445C0DD,0x586CDECF,0x1C20C8AE,0x5BBEF7DD,0x1B588D40,0xCCD2017F,0x6BB4E3BB,
+ 0xDDA26A7E,0x3A59FF45,0x3E350A44,0xBCB4CDD5,0x72EACEA8,0xFA6484BB,0x8D6612AE,0xBF3C6F47,
+ 0xD29BE463,0x542F5D9E,0xAEC2771B,0xF64E6370,0x740E0D8D,0xE75B1357,0xF8721671,0xAF537D5D,
+ 0x4040CB08,0x4EB4E2CC,0x34D2466A,0x0115AF84,0xE1B00428,0x95983A1D,0x06B89FB4,0xCE6EA048,
+ 0x6F3F3B82,0x3520AB82,0x011A1D4B,0x277227F8,0x611560B1,0xE7933FDC,0xBB3A792B,0x344525BD,
+ 0xA08839E1,0x51CE794B,0x2F32C9B7,0xA01FBAC9,0xE01CC87E,0xBCC7D1F6,0xCF0111C3,0xA1E8AAC7,
+ 0x1A908749,0xD44FBD9A,0xD0DADECB,0xD50ADA38,0x0339C32A,0xC6913667,0x8DF9317C,0xE0B12B4F,
+ 0xF79E59B7,0x43F5BB3A,0xF2D519FF,0x27D9459C,0xBF97222C,0x15E6FC2A,0x0F91FC71,0x9B941525,
+ 0xFAE59361,0xCEB69CEB,0xC2A86459,0x12BAA8D1,0xB6C1075E,0xE3056A0C,0x10D25065,0xCB03A442,
+ 0xE0EC6E0E,0x1698DB3B,0x4C98A0BE,0x3278E964,0x9F1F9532,0xE0D392DF,0xD3A0342B,0x8971F21E,
+ 0x1B0A7441,0x4BA3348C,0xC5BE7120,0xC37632D8,0xDF359F8D,0x9B992F2E,0xE60B6F47,0x0FE3F11D,
+ 0xE54CDA54,0x1EDAD891,0xCE6279CF,0xCD3E7E6F,0x1618B166,0xFD2C1D05,0x848FD2C5,0xF6FB2299,
+ 0xF523F357,0xA6327623,0x93A83531,0x56CCCD02,0xACF08162,0x5A75EBB5,0x6E163697,0x88D273CC,
+ 0xDE966292,0x81B949D0,0x4C50901B,0x71C65614,0xE6C6C7BD,0x327A140A,0x45E1D006,0xC3F27B9A,
+ 0xC9AA53FD,0x62A80F00,0xBB25BFE2,0x35BDD2F6,0x71126905,0xB2040222,0xB6CBCF7C,0xCD769C2B,
+ 0x53113EC0,0x1640E3D3,0x38ABBD60,0x2547ADF0,0xBA38209C,0xF746CE76,0x77AFA1C5,0x20756060,
+ 0x85CBFE4E,0x8AE88DD8,0x7AAAF9B0,0x4CF9AA7E,0x1948C25C,0x02FB8A8C,0x01C36AE4,0xD6EBE1F9,
+ 0x90D4F869,0xA65CDEA0,0x3F09252D,0xC208E69F,0xB74E6132,0xCE77E25B,0x578FDFE3,0x3AC372E6
+} };
+
+/*********************** FUNCTION DEFINITIONS ***********************/
+void blowfish_encrypt(const BYTE in[], BYTE out[], const BLOWFISH_KEY *keystruct)
+{
+ WORD l,r,t; //,i;
+
+ l = (in[0] << 24) | (in[1] << 16) | (in[2] << 8) | (in[3]);
+ r = (in[4] << 24) | (in[5] << 16) | (in[6] << 8) | (in[7]);
+
+ ITERATION(l,r,t,0);
+ ITERATION(l,r,t,1);
+ ITERATION(l,r,t,2);
+ ITERATION(l,r,t,3);
+ ITERATION(l,r,t,4);
+ ITERATION(l,r,t,5);
+ ITERATION(l,r,t,6);
+ ITERATION(l,r,t,7);
+ ITERATION(l,r,t,8);
+ ITERATION(l,r,t,9);
+ ITERATION(l,r,t,10);
+ ITERATION(l,r,t,11);
+ ITERATION(l,r,t,12);
+ ITERATION(l,r,t,13);
+ ITERATION(l,r,t,14);
+ l ^= keystruct->p[15]; F(l,t); r^= t; //Last iteration has no swap()
+ r ^= keystruct->p[16];
+ l ^= keystruct->p[17];
+
+ out[0] = l >> 24;
+ out[1] = l >> 16;
+ out[2] = l >> 8;
+ out[3] = l;
+ out[4] = r >> 24;
+ out[5] = r >> 16;
+ out[6] = r >> 8;
+ out[7] = r;
+}
+
+void blowfish_decrypt(const BYTE in[], BYTE out[], const BLOWFISH_KEY *keystruct)
+{
+ WORD l,r,t; //,i;
+
+ l = (in[0] << 24) | (in[1] << 16) | (in[2] << 8) | (in[3]);
+ r = (in[4] << 24) | (in[5] << 16) | (in[6] << 8) | (in[7]);
+
+ ITERATION(l,r,t,17);
+ ITERATION(l,r,t,16);
+ ITERATION(l,r,t,15);
+ ITERATION(l,r,t,14);
+ ITERATION(l,r,t,13);
+ ITERATION(l,r,t,12);
+ ITERATION(l,r,t,11);
+ ITERATION(l,r,t,10);
+ ITERATION(l,r,t,9);
+ ITERATION(l,r,t,8);
+ ITERATION(l,r,t,7);
+ ITERATION(l,r,t,6);
+ ITERATION(l,r,t,5);
+ ITERATION(l,r,t,4);
+ ITERATION(l,r,t,3);
+ l ^= keystruct->p[2]; F(l,t); r^= t; //Last iteration has no swap()
+ r ^= keystruct->p[1];
+ l ^= keystruct->p[0];
+
+ out[0] = l >> 24;
+ out[1] = l >> 16;
+ out[2] = l >> 8;
+ out[3] = l;
+ out[4] = r >> 24;
+ out[5] = r >> 16;
+ out[6] = r >> 8;
+ out[7] = r;
+}
+
+void blowfish_key_setup(const BYTE user_key[], BLOWFISH_KEY *keystruct, size_t len)
+{
+ BYTE block[8];
+ int idx,idx2;
+
+ // Copy over the constant init array vals (so the originals aren't destroyed).
+ memcpy(keystruct->p,p_perm,sizeof(WORD) * 18);
+ memcpy(keystruct->s,s_perm,sizeof(WORD) * 1024);
+
+ // Combine the key with the P box. Assume key is standard 448 bits (56 bytes) or less.
+ for (idx = 0, idx2 = 0; idx < 18; ++idx, idx2 += 4)
+ keystruct->p[idx] ^= (user_key[idx2 % len] << 24) | (user_key[(idx2+1) % len] << 16)
+ | (user_key[(idx2+2) % len] << 8) | (user_key[(idx2+3) % len]);
+ // Re-calculate the P box.
+ memset(block, 0, 8);
+ for (idx = 0; idx < 18; idx += 2) {
+ blowfish_encrypt(block,block,keystruct);
+ keystruct->p[idx] = (block[0] << 24) | (block[1] << 16) | (block[2] << 8) | block[3];
+ keystruct->p[idx+1]=(block[4] << 24) | (block[5] << 16) | (block[6] << 8) | block[7];
+ }
+ // Recalculate the S-boxes.
+ for (idx = 0; idx < 4; ++idx) {
+ for (idx2 = 0; idx2 < 256; idx2 += 2) {
+ blowfish_encrypt(block,block,keystruct);
+ keystruct->s[idx][idx2] = (block[0] << 24) | (block[1] << 16) |
+ (block[2] << 8) | block[3];
+ keystruct->s[idx][idx2+1] = (block[4] << 24) | (block[5] << 16) |
+ (block[6] << 8) | block[7];
+ }
+ }
+}
--- /dev/null
+/*********************************************************************
+* Filename: blowfish.h
+* Author: Brad Conte (brad AT bradconte.com)
+* Copyright:
+* Disclaimer: This code is presented "as is" without any guarantees.
+* Details: Defines the API for the corresponding Blowfish implementation.
+*********************************************************************/
+
+#ifndef BLOWFISH_H
+#define BLOWFISH_H
+
+/*************************** HEADER FILES ***************************/
+#include <stddef.h>
+
+/****************************** MACROS ******************************/
+#define BLOWFISH_BLOCK_SIZE 8 // Blowfish operates on 8 bytes at a time
+
+/**************************** DATA TYPES ****************************/
+typedef unsigned char BYTE; // 8-bit byte
+typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines
+
+typedef struct {
+ WORD p[18];
+ WORD s[4][256];
+} BLOWFISH_KEY;
+
+/*********************** FUNCTION DECLARATIONS **********************/
+void blowfish_key_setup(const BYTE user_key[], BLOWFISH_KEY *keystruct, size_t len);
+void blowfish_encrypt(const BYTE in[], BYTE out[], const BLOWFISH_KEY *keystruct);
+void blowfish_decrypt(const BYTE in[], BYTE out[], const BLOWFISH_KEY *keystruct);
+
+#endif // BLOWFISH_H
--- /dev/null
+/*********************************************************************
+* Filename: blowfish_test.c
+* Author: Brad Conte (brad AT bradconte.com)
+* Copyright:
+* Disclaimer: This code is presented "as is" without any guarantees.
+* Details: Performs known-answer tests on the corresponding Blowfish
+ implementation. These tests do not encompass the full
+ range of available test vectors, however, if the tests
+ pass it is very, very likely that the code is correct
+ and was compiled properly. This code also serves as
+ example usage of the functions.
+*********************************************************************/
+
+/*************************** HEADER FILES ***************************/
+#include <stdio.h>
+#include <memory.h>
+#include "blowfish.h"
+
+/*********************** FUNCTION DEFINITIONS ***********************/
+int blowfish_test()
+{
+ BYTE key1[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
+ BYTE key2[8] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
+ BYTE key3[24] = {0xF0,0xE1,0xD2,0xC3,0xB4,0xA5,0x96,0x87,
+ 0x78,0x69,0x5A,0x4B,0x3C,0x2D,0x1E,0x0F,
+ 0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77};
+ BYTE p1[BLOWFISH_BLOCK_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
+ BYTE p2[BLOWFISH_BLOCK_SIZE] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
+ BYTE p3[BLOWFISH_BLOCK_SIZE] = {0xFE,0xDC,0xBA,0x98,0x76,0x54,0x32,0x10};
+
+ BYTE c1[BLOWFISH_BLOCK_SIZE] = {0x4e,0xf9,0x97,0x45,0x61,0x98,0xdd,0x78};
+ BYTE c2[BLOWFISH_BLOCK_SIZE] = {0x51,0x86,0x6f,0xd5,0xb8,0x5e,0xcb,0x8a};
+ BYTE c3[BLOWFISH_BLOCK_SIZE] = {0x05,0x04,0x4b,0x62,0xfa,0x52,0xd0,0x80};
+
+ BYTE enc_buf[BLOWFISH_BLOCK_SIZE];
+ BLOWFISH_KEY key;
+ int pass = 1;
+
+ // Test vector 1.
+ blowfish_key_setup(key1, &key, BLOWFISH_BLOCK_SIZE);
+ blowfish_encrypt(p1, enc_buf, &key);
+ pass = pass && !memcmp(c1, enc_buf, BLOWFISH_BLOCK_SIZE);
+ blowfish_decrypt(c1, enc_buf, &key);
+ pass = pass && !memcmp(p1, enc_buf, BLOWFISH_BLOCK_SIZE);
+
+ // Test vector 2.
+ blowfish_key_setup(key2, &key, BLOWFISH_BLOCK_SIZE);
+ blowfish_encrypt(p2, enc_buf, &key);
+ pass = pass && !memcmp(c2, enc_buf, BLOWFISH_BLOCK_SIZE);
+ blowfish_decrypt(c2, enc_buf, &key);
+ pass = pass && !memcmp(p2, enc_buf, BLOWFISH_BLOCK_SIZE);
+
+ // Test vector 3.
+ blowfish_key_setup(key3, &key, 24);
+ blowfish_encrypt(p3, enc_buf, &key);
+ pass = pass && !memcmp(c3, enc_buf, BLOWFISH_BLOCK_SIZE);
+ blowfish_decrypt(c3, enc_buf, &key);
+ pass = pass && !memcmp(p3, enc_buf, BLOWFISH_BLOCK_SIZE);
+
+ return(pass);
+}
+
+int main()
+{
+ printf("Blowfish tests: %s\n", blowfish_test() ? "SUCCEEDED" : "FAILED");
+
+ return(0);
+}
--- /dev/null
+/*********************************************************************
+* Filename: des.c
+* Author: Brad Conte (brad AT radconte.com)
+* Copyright:
+* Disclaimer: This code is presented "as is" without any guarantees.
+* Details: Implementation of the DES encryption algorithm.
+ Modes of operation (such as CBC) are not included.
+ The formal NIST algorithm specification can be found here:
+ * http://csrc.nist.gov/publications/fips/fips46-3/fips46-3.pdf
+*********************************************************************/
+
+/*************************** HEADER FILES ***************************/
+#include <stdlib.h>
+#include <memory.h>
+#include "des.h"
+
+/****************************** MACROS ******************************/
+// Obtain bit "b" from the left and shift it "c" places from the right
+#define BITNUM(a,b,c) (((a[(b)/8] >> (7 - (b%8))) & 0x01) << (c))
+#define BITNUMINTR(a,b,c) ((((a) >> (31 - (b))) & 0x00000001) << (c))
+#define BITNUMINTL(a,b,c) ((((a) << (b)) & 0x80000000) >> (c))
+
+// This macro converts a 6 bit block with the S-Box row defined as the first and last
+// bits to a 6 bit block with the row defined by the first two bits.
+#define SBOXBIT(a) (((a) & 0x20) | (((a) & 0x1f) >> 1) | (((a) & 0x01) << 4))
+
+/**************************** VARIABLES *****************************/
+static const BYTE sbox1[64] = {
+ 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
+ 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
+ 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
+ 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13
+};
+static const BYTE sbox2[64] = {
+ 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
+ 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
+ 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
+ 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9
+};
+static const BYTE sbox3[64] = {
+ 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
+ 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
+ 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
+ 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12
+};
+static const BYTE sbox4[64] = {
+ 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
+ 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
+ 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
+ 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14
+};
+static const BYTE sbox5[64] = {
+ 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
+ 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
+ 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
+ 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3
+};
+static const BYTE sbox6[64] = {
+ 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
+ 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
+ 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
+ 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13
+};
+static const BYTE sbox7[64] = {
+ 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
+ 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
+ 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
+ 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12
+};
+static const BYTE sbox8[64] = {
+ 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
+ 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
+ 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
+ 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11
+};
+
+/*********************** FUNCTION DEFINITIONS ***********************/
+// Initial (Inv)Permutation step
+void IP(WORD state[], const BYTE in[])
+{
+ state[0] = BITNUM(in,57,31) | BITNUM(in,49,30) | BITNUM(in,41,29) | BITNUM(in,33,28) |
+ BITNUM(in,25,27) | BITNUM(in,17,26) | BITNUM(in,9,25) | BITNUM(in,1,24) |
+ BITNUM(in,59,23) | BITNUM(in,51,22) | BITNUM(in,43,21) | BITNUM(in,35,20) |
+ BITNUM(in,27,19) | BITNUM(in,19,18) | BITNUM(in,11,17) | BITNUM(in,3,16) |
+ BITNUM(in,61,15) | BITNUM(in,53,14) | BITNUM(in,45,13) | BITNUM(in,37,12) |
+ BITNUM(in,29,11) | BITNUM(in,21,10) | BITNUM(in,13,9) | BITNUM(in,5,8) |
+ BITNUM(in,63,7) | BITNUM(in,55,6) | BITNUM(in,47,5) | BITNUM(in,39,4) |
+ BITNUM(in,31,3) | BITNUM(in,23,2) | BITNUM(in,15,1) | BITNUM(in,7,0);
+
+ state[1] = BITNUM(in,56,31) | BITNUM(in,48,30) | BITNUM(in,40,29) | BITNUM(in,32,28) |
+ BITNUM(in,24,27) | BITNUM(in,16,26) | BITNUM(in,8,25) | BITNUM(in,0,24) |
+ BITNUM(in,58,23) | BITNUM(in,50,22) | BITNUM(in,42,21) | BITNUM(in,34,20) |
+ BITNUM(in,26,19) | BITNUM(in,18,18) | BITNUM(in,10,17) | BITNUM(in,2,16) |
+ BITNUM(in,60,15) | BITNUM(in,52,14) | BITNUM(in,44,13) | BITNUM(in,36,12) |
+ BITNUM(in,28,11) | BITNUM(in,20,10) | BITNUM(in,12,9) | BITNUM(in,4,8) |
+ BITNUM(in,62,7) | BITNUM(in,54,6) | BITNUM(in,46,5) | BITNUM(in,38,4) |
+ BITNUM(in,30,3) | BITNUM(in,22,2) | BITNUM(in,14,1) | BITNUM(in,6,0);
+}
+
+void InvIP(WORD state[], BYTE in[])
+{
+ in[0] = BITNUMINTR(state[1],7,7) | BITNUMINTR(state[0],7,6) | BITNUMINTR(state[1],15,5) |
+ BITNUMINTR(state[0],15,4) | BITNUMINTR(state[1],23,3) | BITNUMINTR(state[0],23,2) |
+ BITNUMINTR(state[1],31,1) | BITNUMINTR(state[0],31,0);
+
+ in[1] = BITNUMINTR(state[1],6,7) | BITNUMINTR(state[0],6,6) | BITNUMINTR(state[1],14,5) |
+ BITNUMINTR(state[0],14,4) | BITNUMINTR(state[1],22,3) | BITNUMINTR(state[0],22,2) |
+ BITNUMINTR(state[1],30,1) | BITNUMINTR(state[0],30,0);
+
+ in[2] = BITNUMINTR(state[1],5,7) | BITNUMINTR(state[0],5,6) | BITNUMINTR(state[1],13,5) |
+ BITNUMINTR(state[0],13,4) | BITNUMINTR(state[1],21,3) | BITNUMINTR(state[0],21,2) |
+ BITNUMINTR(state[1],29,1) | BITNUMINTR(state[0],29,0);
+
+ in[3] = BITNUMINTR(state[1],4,7) | BITNUMINTR(state[0],4,6) | BITNUMINTR(state[1],12,5) |
+ BITNUMINTR(state[0],12,4) | BITNUMINTR(state[1],20,3) | BITNUMINTR(state[0],20,2) |
+ BITNUMINTR(state[1],28,1) | BITNUMINTR(state[0],28,0);
+
+ in[4] = BITNUMINTR(state[1],3,7) | BITNUMINTR(state[0],3,6) | BITNUMINTR(state[1],11,5) |
+ BITNUMINTR(state[0],11,4) | BITNUMINTR(state[1],19,3) | BITNUMINTR(state[0],19,2) |
+ BITNUMINTR(state[1],27,1) | BITNUMINTR(state[0],27,0);
+
+ in[5] = BITNUMINTR(state[1],2,7) | BITNUMINTR(state[0],2,6) | BITNUMINTR(state[1],10,5) |
+ BITNUMINTR(state[0],10,4) | BITNUMINTR(state[1],18,3) | BITNUMINTR(state[0],18,2) |
+ BITNUMINTR(state[1],26,1) | BITNUMINTR(state[0],26,0);
+
+ in[6] = BITNUMINTR(state[1],1,7) | BITNUMINTR(state[0],1,6) | BITNUMINTR(state[1],9,5) |
+ BITNUMINTR(state[0],9,4) | BITNUMINTR(state[1],17,3) | BITNUMINTR(state[0],17,2) |
+ BITNUMINTR(state[1],25,1) | BITNUMINTR(state[0],25,0);
+
+ in[7] = BITNUMINTR(state[1],0,7) | BITNUMINTR(state[0],0,6) | BITNUMINTR(state[1],8,5) |
+ BITNUMINTR(state[0],8,4) | BITNUMINTR(state[1],16,3) | BITNUMINTR(state[0],16,2) |
+ BITNUMINTR(state[1],24,1) | BITNUMINTR(state[0],24,0);
+}
+
+WORD f(WORD state, const BYTE key[])
+{
+ BYTE lrgstate[6]; //,i;
+ WORD t1,t2;
+
+ // Expantion Permutation
+ t1 = BITNUMINTL(state,31,0) | ((state & 0xf0000000) >> 1) | BITNUMINTL(state,4,5) |
+ BITNUMINTL(state,3,6) | ((state & 0x0f000000) >> 3) | BITNUMINTL(state,8,11) |
+ BITNUMINTL(state,7,12) | ((state & 0x00f00000) >> 5) | BITNUMINTL(state,12,17) |
+ BITNUMINTL(state,11,18) | ((state & 0x000f0000) >> 7) | BITNUMINTL(state,16,23);
+
+ t2 = BITNUMINTL(state,15,0) | ((state & 0x0000f000) << 15) | BITNUMINTL(state,20,5) |
+ BITNUMINTL(state,19,6) | ((state & 0x00000f00) << 13) | BITNUMINTL(state,24,11) |
+ BITNUMINTL(state,23,12) | ((state & 0x000000f0) << 11) | BITNUMINTL(state,28,17) |
+ BITNUMINTL(state,27,18) | ((state & 0x0000000f) << 9) | BITNUMINTL(state,0,23);
+
+ lrgstate[0] = (t1 >> 24) & 0x000000ff;
+ lrgstate[1] = (t1 >> 16) & 0x000000ff;
+ lrgstate[2] = (t1 >> 8) & 0x000000ff;
+ lrgstate[3] = (t2 >> 24) & 0x000000ff;
+ lrgstate[4] = (t2 >> 16) & 0x000000ff;
+ lrgstate[5] = (t2 >> 8) & 0x000000ff;
+
+ // Key XOR
+ lrgstate[0] ^= key[0];
+ lrgstate[1] ^= key[1];
+ lrgstate[2] ^= key[2];
+ lrgstate[3] ^= key[3];
+ lrgstate[4] ^= key[4];
+ lrgstate[5] ^= key[5];
+
+ // S-Box Permutation
+ state = (sbox1[SBOXBIT(lrgstate[0] >> 2)] << 28) |
+ (sbox2[SBOXBIT(((lrgstate[0] & 0x03) << 4) | (lrgstate[1] >> 4))] << 24) |
+ (sbox3[SBOXBIT(((lrgstate[1] & 0x0f) << 2) | (lrgstate[2] >> 6))] << 20) |
+ (sbox4[SBOXBIT(lrgstate[2] & 0x3f)] << 16) |
+ (sbox5[SBOXBIT(lrgstate[3] >> 2)] << 12) |
+ (sbox6[SBOXBIT(((lrgstate[3] & 0x03) << 4) | (lrgstate[4] >> 4))] << 8) |
+ (sbox7[SBOXBIT(((lrgstate[4] & 0x0f) << 2) | (lrgstate[5] >> 6))] << 4) |
+ sbox8[SBOXBIT(lrgstate[5] & 0x3f)];
+
+ // P-Box Permutation
+ state = BITNUMINTL(state,15,0) | BITNUMINTL(state,6,1) | BITNUMINTL(state,19,2) |
+ BITNUMINTL(state,20,3) | BITNUMINTL(state,28,4) | BITNUMINTL(state,11,5) |
+ BITNUMINTL(state,27,6) | BITNUMINTL(state,16,7) | BITNUMINTL(state,0,8) |
+ BITNUMINTL(state,14,9) | BITNUMINTL(state,22,10) | BITNUMINTL(state,25,11) |
+ BITNUMINTL(state,4,12) | BITNUMINTL(state,17,13) | BITNUMINTL(state,30,14) |
+ BITNUMINTL(state,9,15) | BITNUMINTL(state,1,16) | BITNUMINTL(state,7,17) |
+ BITNUMINTL(state,23,18) | BITNUMINTL(state,13,19) | BITNUMINTL(state,31,20) |
+ BITNUMINTL(state,26,21) | BITNUMINTL(state,2,22) | BITNUMINTL(state,8,23) |
+ BITNUMINTL(state,18,24) | BITNUMINTL(state,12,25) | BITNUMINTL(state,29,26) |
+ BITNUMINTL(state,5,27) | BITNUMINTL(state,21,28) | BITNUMINTL(state,10,29) |
+ BITNUMINTL(state,3,30) | BITNUMINTL(state,24,31);
+
+ // Return the final state value
+ return(state);
+}
+
+void des_key_setup(const BYTE key[], BYTE schedule[][6], DES_MODE mode)
+{
+ WORD i, j, to_gen, C, D;
+ const WORD key_rnd_shift[16] = {1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1};
+ const WORD key_perm_c[28] = {56,48,40,32,24,16,8,0,57,49,41,33,25,17,
+ 9,1,58,50,42,34,26,18,10,2,59,51,43,35};
+ const WORD key_perm_d[28] = {62,54,46,38,30,22,14,6,61,53,45,37,29,21,
+ 13,5,60,52,44,36,28,20,12,4,27,19,11,3};
+ const WORD key_compression[48] = {13,16,10,23,0,4,2,27,14,5,20,9,
+ 22,18,11,3,25,7,15,6,26,19,12,1,
+ 40,51,30,36,46,54,29,39,50,44,32,47,
+ 43,48,38,55,33,52,45,41,49,35,28,31};
+
+ // Permutated Choice #1 (copy the key in, ignoring parity bits).
+ for (i = 0, j = 31, C = 0; i < 28; ++i, --j)
+ C |= BITNUM(key,key_perm_c[i],j);
+ for (i = 0, j = 31, D = 0; i < 28; ++i, --j)
+ D |= BITNUM(key,key_perm_d[i],j);
+
+ // Generate the 16 subkeys.
+ for (i = 0; i < 16; ++i) {
+ C = ((C << key_rnd_shift[i]) | (C >> (28-key_rnd_shift[i]))) & 0xfffffff0;
+ D = ((D << key_rnd_shift[i]) | (D >> (28-key_rnd_shift[i]))) & 0xfffffff0;
+
+ // Decryption subkeys are reverse order of encryption subkeys so
+ // generate them in reverse if the key schedule is for decryption useage.
+ if (mode == DES_DECRYPT)
+ to_gen = 15 - i;
+ else /*(if mode == DES_ENCRYPT)*/
+ to_gen = i;
+ // Initialize the array
+ for (j = 0; j < 6; ++j)
+ schedule[to_gen][j] = 0;
+ for (j = 0; j < 24; ++j)
+ schedule[to_gen][j/8] |= BITNUMINTR(C,key_compression[j],7 - (j%8));
+ for ( ; j < 48; ++j)
+ schedule[to_gen][j/8] |= BITNUMINTR(D,key_compression[j] - 28,7 - (j%8));
+ }
+}
+
+void des_crypt(const BYTE in[], BYTE out[], const BYTE key[][6])
+{
+ WORD state[2],idx,t;
+
+ IP(state,in);
+
+ for (idx=0; idx < 15; ++idx) {
+ t = state[1];
+ state[1] = f(state[1],key[idx]) ^ state[0];
+ state[0] = t;
+ }
+ // Perform the final loop manually as it doesn't switch sides
+ state[0] = f(state[1],key[15]) ^ state[0];
+
+ InvIP(state,out);
+}
+
+void three_des_key_setup(const BYTE key[], BYTE schedule[][16][6], DES_MODE mode)
+{
+ if (mode == DES_ENCRYPT) {
+ des_key_setup(&key[0],schedule[0],mode);
+ des_key_setup(&key[8],schedule[1],!mode);
+ des_key_setup(&key[16],schedule[2],mode);
+ }
+ else /*if (mode == DES_DECRYPT*/ {
+ des_key_setup(&key[16],schedule[0],mode);
+ des_key_setup(&key[8],schedule[1],!mode);
+ des_key_setup(&key[0],schedule[2],mode);
+ }
+}
+
+void three_des_crypt(const BYTE in[], BYTE out[], const BYTE key[][16][6])
+{
+ des_crypt(in,out,key[0]);
+ des_crypt(out,out,key[1]);
+ des_crypt(out,out,key[2]);
+}
--- /dev/null
+/*********************************************************************
+* Filename: des.h
+* Author: Brad Conte (brad AT bradconte.com)
+* Copyright:
+* Disclaimer: This code is presented "as is" without any guarantees.
+* Details: Defines the API for the corresponding DES implementation.
+ Note that encryption and decryption are defined by how
+ the key setup is performed, the actual en/de-cryption is
+ performed by the same function.
+*********************************************************************/
+
+#ifndef DES_H
+#define DESH
+
+/*************************** HEADER FILES ***************************/
+#include <stddef.h>
+
+/****************************** MACROS ******************************/
+#define DES_BLOCK_SIZE 8 // DES operates on 8 bytes at a time
+
+/**************************** DATA TYPES ****************************/
+typedef unsigned char BYTE; // 8-bit byte
+typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines
+
+typedef enum {
+ DES_ENCRYPT,
+ DES_DECRYPT
+} DES_MODE;
+
+/*********************** FUNCTION DECLARATIONS **********************/
+void des_key_setup(const BYTE key[], BYTE schedule[][6], DES_MODE mode);
+void des_crypt(const BYTE in[], BYTE out[], const BYTE key[][6]);
+
+void three_des_key_setup(const BYTE key[], BYTE schedule[][16][6], DES_MODE mode);
+void three_des_crypt(const BYTE in[], BYTE out[], const BYTE key[][16][6]);
+
+#endif // DES_H
--- /dev/null
+/*********************************************************************
+* Filename: des_test.c
+* Author: Brad Conte (brad AT bradconte.com)
+* Copyright:
+* Disclaimer: This code is presented "as is" without any guarantees.
+* Details: Performs known-answer tests on the corresponding DES
+ implementation. These tests do not encompass the full
+ range of available test vectors, however, if the tests
+ pass it is very, very likely that the code is correct
+ and was compiled properly. This code also serves as
+ example usage of the functions.
+*********************************************************************/
+
+/*************************** HEADER FILES ***************************/
+#include <stdio.h>
+#include <memory.h>
+#include "des.h"
+
+/*********************** FUNCTION DEFINITIONS ***********************/
+int des_test()
+{
+ BYTE pt1[DES_BLOCK_SIZE] = {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xE7};
+ BYTE pt2[DES_BLOCK_SIZE] = {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF};
+ BYTE pt3[DES_BLOCK_SIZE] = {0x54,0x68,0x65,0x20,0x71,0x75,0x66,0x63};
+ BYTE ct1[DES_BLOCK_SIZE] = {0xc9,0x57,0x44,0x25,0x6a,0x5e,0xd3,0x1d};
+ BYTE ct2[DES_BLOCK_SIZE] = {0x85,0xe8,0x13,0x54,0x0f,0x0a,0xb4,0x05};
+ BYTE ct3[DES_BLOCK_SIZE] = {0xc9,0x57,0x44,0x25,0x6a,0x5e,0xd3,0x1d};
+ BYTE ct4[DES_BLOCK_SIZE] = {0xA8,0x26,0xFD,0x8C,0xE5,0x3B,0x85,0x5F};
+ BYTE key1[DES_BLOCK_SIZE] = {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF};
+ BYTE key2[DES_BLOCK_SIZE] = {0x13,0x34,0x57,0x79,0x9B,0xBC,0xDF,0xF1};
+ BYTE three_key1[DES_BLOCK_SIZE * 3] = {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF,
+ 0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF,
+ 0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF};
+ BYTE three_key2[DES_BLOCK_SIZE * 3] = {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF,
+ 0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF,0x01,
+ 0x45,0x67,0x89,0xAB,0xCD,0xEF,0x01,0x23};
+
+ BYTE schedule[16][6];
+ BYTE three_schedule[3][16][6];
+ BYTE buf[DES_BLOCK_SIZE];
+ int pass = 1;
+
+ des_key_setup(key1, schedule, DES_ENCRYPT);
+ des_crypt(pt1, buf, schedule);
+ pass = pass && !memcmp(ct1, buf, DES_BLOCK_SIZE);
+
+ des_key_setup(key1, schedule, DES_DECRYPT);
+ des_crypt(ct1, buf, schedule);
+ pass = pass && !memcmp(pt1, buf, DES_BLOCK_SIZE);
+
+ des_key_setup(key2, schedule, DES_ENCRYPT);
+ des_crypt(pt2, buf, schedule);
+ pass = pass && !memcmp(ct2, buf, DES_BLOCK_SIZE);
+
+ des_key_setup(key2, schedule, DES_DECRYPT);
+ des_crypt(ct2, buf, schedule);
+ pass = pass && !memcmp(pt2, buf, DES_BLOCK_SIZE);
+
+ three_des_key_setup(three_key1, three_schedule, DES_ENCRYPT);
+ three_des_crypt(pt1, buf, three_schedule);
+ pass = pass && !memcmp(ct3, buf, DES_BLOCK_SIZE);
+
+ three_des_key_setup(three_key1, three_schedule, DES_DECRYPT);
+ three_des_crypt(ct3, buf, three_schedule);
+ pass = pass && !memcmp(pt1, buf, DES_BLOCK_SIZE);
+
+ three_des_key_setup(three_key2, three_schedule, DES_ENCRYPT);
+ three_des_crypt(pt3, buf, three_schedule);
+ pass = pass && !memcmp(ct4, buf, DES_BLOCK_SIZE);
+
+ three_des_key_setup(three_key2, three_schedule, DES_DECRYPT);
+ three_des_crypt(ct4, buf, three_schedule);
+ pass = pass && !memcmp(pt3, buf, DES_BLOCK_SIZE);
+
+ return(pass);
+}
+
+int main()
+{
+ printf("DES test: %s\n", des_test() ? "SUCCEEDED" : "FAILED");
+
+ return(0);
+}
--- /dev/null
+/*********************************************************************
+* Filename: md2.c
+* Author: Brad Conte (brad AT bradconte.com)
+* Copyright:
+* Disclaimer: This code is presented "as is" without any guarantees.
+* Details: Implementation of the MD2 hashing algorithm.
+ Algorithm specification can be found here:
+ * http://tools.ietf.org/html/rfc1319 .
+ Input is little endian byte order.
+*********************************************************************/
+
+/*************************** HEADER FILES ***************************/
+#include <stdlib.h>
+#include <memory.h>
+#include "md2.h"
+
+/**************************** VARIABLES *****************************/
+static const BYTE s[256] = {
+ 41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6,
+ 19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188,
+ 76, 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24,
+ 138, 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251,
+ 245, 142, 187, 47, 238, 122, 169, 104, 121, 145, 21, 178, 7, 63,
+ 148, 194, 16, 137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50,
+ 39, 53, 62, 204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165,
+ 181, 209, 215, 94, 146, 42, 172, 86, 170, 198, 79, 184, 56, 210,
+ 150, 164, 125, 182, 118, 252, 107, 226, 156, 116, 4, 241, 69, 157,
+ 112, 89, 100, 113, 135, 32, 134, 91, 207, 101, 230, 45, 168, 2, 27,
+ 96, 37, 173, 174, 176, 185, 246, 28, 70, 97, 105, 52, 64, 126, 15,
+ 85, 71, 163, 35, 221, 81, 175, 58, 195, 92, 249, 206, 186, 197,
+ 234, 38, 44, 83, 13, 110, 133, 40, 132, 9, 211, 223, 205, 244, 65,
+ 129, 77, 82, 106, 220, 55, 200, 108, 193, 171, 250, 36, 225, 123,
+ 8, 12, 189, 177, 74, 120, 136, 149, 139, 227, 99, 232, 109, 233,
+ 203, 213, 254, 59, 0, 29, 57, 242, 239, 183, 14, 102, 88, 208, 228,
+ 166, 119, 114, 248, 235, 117, 75, 10, 49, 68, 80, 180, 143, 237,
+ 31, 26, 219, 153, 141, 51, 159, 17, 131, 20
+};
+
+/*********************** FUNCTION DEFINITIONS ***********************/
+void md2_transform(MD2_CTX *ctx, BYTE data[])
+{
+ int j,k,t;
+
+ //memcpy(&ctx->state[16], data);
+ for (j=0; j < 16; ++j) {
+ ctx->state[j + 16] = data[j];
+ ctx->state[j + 32] = (ctx->state[j+16] ^ ctx->state[j]);
+ }
+
+ t = 0;
+ for (j = 0; j < 18; ++j) {
+ for (k = 0; k < 48; ++k) {
+ ctx->state[k] ^= s[t];
+ t = ctx->state[k];
+ }
+ t = (t+j) & 0xFF;
+ }
+
+ t = ctx->checksum[15];
+ for (j=0; j < 16; ++j) {
+ ctx->checksum[j] ^= s[data[j] ^ t];
+ t = ctx->checksum[j];
+ }
+}
+
+void md2_init(MD2_CTX *ctx)
+{
+ int i;
+
+ for (i=0; i < 48; ++i)
+ ctx->state[i] = 0;
+ for (i=0; i < 16; ++i)
+ ctx->checksum[i] = 0;
+ ctx->len = 0;
+}
+
+void md2_update(MD2_CTX *ctx, const BYTE data[], size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len; ++i) {
+ ctx->data[ctx->len] = data[i];
+ ctx->len++;
+ if (ctx->len == MD2_BLOCK_SIZE) {
+ md2_transform(ctx, ctx->data);
+ ctx->len = 0;
+ }
+ }
+}
+
+void md2_final(MD2_CTX *ctx, BYTE hash[])
+{
+ int to_pad;
+
+ to_pad = MD2_BLOCK_SIZE - ctx->len;
+
+ while (ctx->len < MD2_BLOCK_SIZE)
+ ctx->data[ctx->len++] = to_pad;
+
+ md2_transform(ctx, ctx->data);
+ md2_transform(ctx, ctx->checksum);
+
+ memcpy(hash, ctx->state, MD2_BLOCK_SIZE);
+}
--- /dev/null
+/*********************************************************************
+* Filename: md2.h
+* Author: Brad Conte (brad AT bradconte.com)
+* Copyright:
+* Disclaimer: This code is presented "as is" without any guarantees.
+* Details: Defines the API for the corresponding MD2 implementation.
+*********************************************************************/
+
+#ifndef MD2_H
+#define MD2_H
+
+/*************************** HEADER FILES ***************************/
+#include <stddef.h>
+
+/****************************** MACROS ******************************/
+#define MD2_BLOCK_SIZE 16
+
+/**************************** DATA TYPES ****************************/
+typedef unsigned char BYTE; // 8-bit byte
+
+typedef struct {
+ BYTE data[16];
+ BYTE state[48];
+ BYTE checksum[16];
+ int len;
+} MD2_CTX;
+
+/*********************** FUNCTION DECLARATIONS **********************/
+void md2_init(MD2_CTX *ctx);
+void md2_update(MD2_CTX *ctx, const BYTE data[], size_t len);
+void md2_final(MD2_CTX *ctx, BYTE hash[]); // size of hash must be MD2_BLOCK_SIZE
+
+#endif // MD2_H
--- /dev/null
+/*********************************************************************
+* Filename: md2_test.c
+* Author: Brad Conte (brad AT bradconte.com)
+* Copyright:
+* Disclaimer: This code is presented "as is" without any guarantees.
+* Details: Performs known-answer tests on the corresponding MD2
+ implementation. These tests do not encompass the full
+ range of available test vectors, however, if the tests
+ pass it is very, very likely that the code is correct
+ and was compiled properly. This code also serves as
+ example usage of the functions.
+*********************************************************************/
+
+/*************************** HEADER FILES ***************************/
+#include <stdio.h>
+#include <string.h>
+#include <memory.h>
+#include "md2.h"
+
+/*********************** FUNCTION DEFINITIONS ***********************/
+int md2_test()
+{
+ BYTE text1[] = {"abc"};
+ BYTE text2[] = {"abcdefghijklmnopqrstuvwxyz"};
+ BYTE text3_1[] = {"ABCDEFGHIJKLMNOPQRSTUVWXYZabcde"};
+ BYTE text3_2[] = {"fghijklmnopqrstuvwxyz0123456789"};
+ BYTE hash1[MD2_BLOCK_SIZE] = {0xda,0x85,0x3b,0x0d,0x3f,0x88,0xd9,0x9b,0x30,0x28,0x3a,0x69,0xe6,0xde,0xd6,0xbb};
+ BYTE hash2[MD2_BLOCK_SIZE] = {0x4e,0x8d,0xdf,0xf3,0x65,0x02,0x92,0xab,0x5a,0x41,0x08,0xc3,0xaa,0x47,0x94,0x0b};
+ BYTE hash3[MD2_BLOCK_SIZE] = {0xda,0x33,0xde,0xf2,0xa4,0x2d,0xf1,0x39,0x75,0x35,0x28,0x46,0xc3,0x03,0x38,0xcd};
+ BYTE buf[16];
+ MD2_CTX ctx;
+ int pass = 1;
+
+ md2_init(&ctx);
+ md2_update(&ctx, text1, strlen(text1));
+ md2_final(&ctx, buf);
+ pass = pass && !memcmp(hash1, buf, MD2_BLOCK_SIZE);
+
+ // Note that the MD2 object can be re-used.
+ md2_init(&ctx);
+ md2_update(&ctx, text2, strlen(text2));
+ md2_final(&ctx, buf);
+ pass = pass && !memcmp(hash2, buf, MD2_BLOCK_SIZE);
+
+ // Note that the data is added in two chunks.
+ md2_init(&ctx);
+ md2_update(&ctx, text3_1, strlen(text3_1));
+ md2_update(&ctx, text3_2, strlen(text3_2));
+ md2_final(&ctx, buf);
+ pass = pass && !memcmp(hash3, buf, MD2_BLOCK_SIZE);
+
+ return(pass);
+}
+
+int main()
+{
+ printf("MD2 tests: %s\n", md2_test() ? "SUCCEEDED" : "FAILED");
+}
--- /dev/null
+/*********************************************************************
+* Filename: md5.c
+* Author: Brad Conte (brad AT bradconte.com)
+* Copyright:
+* Disclaimer: This code is presented "as is" without any guarantees.
+* Details: Implementation of the MD5 hashing algorithm.
+ Algorithm specification can be found here:
+ * http://tools.ietf.org/html/rfc1321
+ This implementation uses little endian byte order.
+*********************************************************************/
+
+/*************************** HEADER FILES ***************************/
+#include <stdlib.h>
+#include <memory.h>
+#include "md5.h"
+
+/****************************** MACROS ******************************/
+#define ROTLEFT(a,b) ((a << b) | (a >> (32-b)))
+
+#define F(x,y,z) ((x & y) | (~x & z))
+#define G(x,y,z) ((x & z) | (y & ~z))
+#define H(x,y,z) (x ^ y ^ z)
+#define I(x,y,z) (y ^ (x | ~z))
+
+#define FF(a,b,c,d,m,s,t) { a += F(b,c,d) + m + t; \
+ a = b + ROTLEFT(a,s); }
+#define GG(a,b,c,d,m,s,t) { a += G(b,c,d) + m + t; \
+ a = b + ROTLEFT(a,s); }
+#define HH(a,b,c,d,m,s,t) { a += H(b,c,d) + m + t; \
+ a = b + ROTLEFT(a,s); }
+#define II(a,b,c,d,m,s,t) { a += I(b,c,d) + m + t; \
+ a = b + ROTLEFT(a,s); }
+
+/*********************** FUNCTION DEFINITIONS ***********************/
+void md5_transform(MD5_CTX *ctx, const BYTE data[])
+{
+ WORD a, b, c, d, m[16], i, j;
+
+ // MD5 specifies big endian byte order, but this implementation assumes a little
+ // endian byte order CPU. Reverse all the bytes upon input, and re-reverse them
+ // on output (in md5_final()).
+ for (i = 0, j = 0; i < 16; ++i, j += 4)
+ m[i] = (data[j]) + (data[j + 1] << 8) + (data[j + 2] << 16) + (data[j + 3] << 24);
+
+ a = ctx->state[0];
+ b = ctx->state[1];
+ c = ctx->state[2];
+ d = ctx->state[3];
+
+ FF(a,b,c,d,m[0], 7,0xd76aa478);
+ FF(d,a,b,c,m[1], 12,0xe8c7b756);
+ FF(c,d,a,b,m[2], 17,0x242070db);
+ FF(b,c,d,a,m[3], 22,0xc1bdceee);
+ FF(a,b,c,d,m[4], 7,0xf57c0faf);
+ FF(d,a,b,c,m[5], 12,0x4787c62a);
+ FF(c,d,a,b,m[6], 17,0xa8304613);
+ FF(b,c,d,a,m[7], 22,0xfd469501);
+ FF(a,b,c,d,m[8], 7,0x698098d8);
+ FF(d,a,b,c,m[9], 12,0x8b44f7af);
+ FF(c,d,a,b,m[10],17,0xffff5bb1);
+ FF(b,c,d,a,m[11],22,0x895cd7be);
+ FF(a,b,c,d,m[12], 7,0x6b901122);
+ FF(d,a,b,c,m[13],12,0xfd987193);
+ FF(c,d,a,b,m[14],17,0xa679438e);
+ FF(b,c,d,a,m[15],22,0x49b40821);
+
+ GG(a,b,c,d,m[1], 5,0xf61e2562);
+ GG(d,a,b,c,m[6], 9,0xc040b340);
+ GG(c,d,a,b,m[11],14,0x265e5a51);
+ GG(b,c,d,a,m[0], 20,0xe9b6c7aa);
+ GG(a,b,c,d,m[5], 5,0xd62f105d);
+ GG(d,a,b,c,m[10], 9,0x02441453);
+ GG(c,d,a,b,m[15],14,0xd8a1e681);
+ GG(b,c,d,a,m[4], 20,0xe7d3fbc8);
+ GG(a,b,c,d,m[9], 5,0x21e1cde6);
+ GG(d,a,b,c,m[14], 9,0xc33707d6);
+ GG(c,d,a,b,m[3], 14,0xf4d50d87);
+ GG(b,c,d,a,m[8], 20,0x455a14ed);
+ GG(a,b,c,d,m[13], 5,0xa9e3e905);
+ GG(d,a,b,c,m[2], 9,0xfcefa3f8);
+ GG(c,d,a,b,m[7], 14,0x676f02d9);
+ GG(b,c,d,a,m[12],20,0x8d2a4c8a);
+
+ HH(a,b,c,d,m[5], 4,0xfffa3942);
+ HH(d,a,b,c,m[8], 11,0x8771f681);
+ HH(c,d,a,b,m[11],16,0x6d9d6122);
+ HH(b,c,d,a,m[14],23,0xfde5380c);
+ HH(a,b,c,d,m[1], 4,0xa4beea44);
+ HH(d,a,b,c,m[4], 11,0x4bdecfa9);
+ HH(c,d,a,b,m[7], 16,0xf6bb4b60);
+ HH(b,c,d,a,m[10],23,0xbebfbc70);
+ HH(a,b,c,d,m[13], 4,0x289b7ec6);
+ HH(d,a,b,c,m[0], 11,0xeaa127fa);
+ HH(c,d,a,b,m[3], 16,0xd4ef3085);
+ HH(b,c,d,a,m[6], 23,0x04881d05);
+ HH(a,b,c,d,m[9], 4,0xd9d4d039);
+ HH(d,a,b,c,m[12],11,0xe6db99e5);
+ HH(c,d,a,b,m[15],16,0x1fa27cf8);
+ HH(b,c,d,a,m[2], 23,0xc4ac5665);
+
+ II(a,b,c,d,m[0], 6,0xf4292244);
+ II(d,a,b,c,m[7], 10,0x432aff97);
+ II(c,d,a,b,m[14],15,0xab9423a7);
+ II(b,c,d,a,m[5], 21,0xfc93a039);
+ II(a,b,c,d,m[12], 6,0x655b59c3);
+ II(d,a,b,c,m[3], 10,0x8f0ccc92);
+ II(c,d,a,b,m[10],15,0xffeff47d);
+ II(b,c,d,a,m[1], 21,0x85845dd1);
+ II(a,b,c,d,m[8], 6,0x6fa87e4f);
+ II(d,a,b,c,m[15],10,0xfe2ce6e0);
+ II(c,d,a,b,m[6], 15,0xa3014314);
+ II(b,c,d,a,m[13],21,0x4e0811a1);
+ II(a,b,c,d,m[4], 6,0xf7537e82);
+ II(d,a,b,c,m[11],10,0xbd3af235);
+ II(c,d,a,b,m[2], 15,0x2ad7d2bb);
+ II(b,c,d,a,m[9], 21,0xeb86d391);
+
+ ctx->state[0] += a;
+ ctx->state[1] += b;
+ ctx->state[2] += c;
+ ctx->state[3] += d;
+}
+
+void md5_init(MD5_CTX *ctx)
+{
+ ctx->datalen = 0;
+ ctx->bitlen = 0;
+ ctx->state[0] = 0x67452301;
+ ctx->state[1] = 0xEFCDAB89;
+ ctx->state[2] = 0x98BADCFE;
+ ctx->state[3] = 0x10325476;
+}
+
+void md5_update(MD5_CTX *ctx, const BYTE data[], size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len; ++i) {
+ ctx->data[ctx->datalen] = data[i];
+ ctx->datalen++;
+ if (ctx->datalen == 64) {
+ md5_transform(ctx, ctx->data);
+ ctx->bitlen += 512;
+ ctx->datalen = 0;
+ }
+ }
+}
+
+void md5_final(MD5_CTX *ctx, BYTE hash[])
+{
+ size_t i;
+
+ i = ctx->datalen;
+
+ // Pad whatever data is left in the buffer.
+ if (ctx->datalen < 56) {
+ ctx->data[i++] = 0x80;
+ while (i < 56)
+ ctx->data[i++] = 0x00;
+ }
+ else if (ctx->datalen >= 56) {
+ ctx->data[i++] = 0x80;
+ while (i < 64)
+ ctx->data[i++] = 0x00;
+ md5_transform(ctx, ctx->data);
+ memset(ctx->data, 0, 56);
+ }
+
+ // Append to the padding the total message's length in bits and transform.
+ ctx->bitlen += ctx->datalen * 8;
+ ctx->data[56] = ctx->bitlen;
+ ctx->data[57] = ctx->bitlen >> 8;
+ ctx->data[58] = ctx->bitlen >> 16;
+ ctx->data[59] = ctx->bitlen >> 24;
+ ctx->data[60] = ctx->bitlen >> 32;
+ ctx->data[61] = ctx->bitlen >> 40;
+ ctx->data[62] = ctx->bitlen >> 48;
+ ctx->data[63] = ctx->bitlen >> 56;
+ md5_transform(ctx, ctx->data);
+
+ // Since this implementation uses little endian byte ordering and MD uses big endian,
+ // reverse all the bytes when copying the final state to the output hash.
+ for (i = 0; i < 4; ++i) {
+ hash[i] = (ctx->state[0] >> (i * 8)) & 0x000000ff;
+ hash[i + 4] = (ctx->state[1] >> (i * 8)) & 0x000000ff;
+ hash[i + 8] = (ctx->state[2] >> (i * 8)) & 0x000000ff;
+ hash[i + 12] = (ctx->state[3] >> (i * 8)) & 0x000000ff;
+ }
+}
--- /dev/null
+/*********************************************************************
+* Filename: md5.h
+* Author: Brad Conte (brad AT bradconte.com)
+* Copyright:
+* Disclaimer: This code is presented "as is" without any guarantees.
+* Details: Defines the API for the corresponding MD5 implementation.
+*********************************************************************/
+
+#ifndef MD5_H
+#define MD5_H
+
+/*************************** HEADER FILES ***************************/
+#include <stddef.h>
+
+/****************************** MACROS ******************************/
+#define MD5_BLOCK_SIZE 16 // MD5 outputs a 16 byte digest
+
+/**************************** DATA TYPES ****************************/
+typedef unsigned char BYTE; // 8-bit byte
+typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines
+
+typedef struct {
+ BYTE data[64];
+ WORD datalen;
+ unsigned long long bitlen;
+ WORD state[4];
+} MD5_CTX;
+
+/*********************** FUNCTION DECLARATIONS **********************/
+void md5_init(MD5_CTX *ctx);
+void md5_update(MD5_CTX *ctx, const BYTE data[], size_t len);
+void md5_final(MD5_CTX *ctx, BYTE hash[]);
+
+#endif // MD5_H
--- /dev/null
+/*********************************************************************
+* Filename: md5_test.c
+* Author: Brad Conte (brad AT bradconte.com)
+* Copyright:
+* Disclaimer: This code is presented "as is" without any guarantees.
+* Details: Performs known-answer tests on the corresponding MD5
+ implementation. These tests do not encompass the full
+ range of available test vectors, however, if the tests
+ pass it is very, very likely that the code is correct
+ and was compiled properly. This code also serves as
+ example usage of the functions.
+*********************************************************************/
+
+/*************************** HEADER FILES ***************************/
+#include <stdio.h>
+#include <memory.h>
+#include <string.h>
+#include "md5.h"
+
+/*********************** FUNCTION DEFINITIONS ***********************/
+int md5_test()
+{
+ BYTE text1[] = {""};
+ BYTE text2[] = {"abc"};
+ BYTE text3_1[] = {"ABCDEFGHIJKLMNOPQRSTUVWXYZabcde"};
+ BYTE text3_2[] = {"fghijklmnopqrstuvwxyz0123456789"};
+ BYTE hash1[MD5_BLOCK_SIZE] = {0xd4,0x1d,0x8c,0xd9,0x8f,0x00,0xb2,0x04,0xe9,0x80,0x09,0x98,0xec,0xf8,0x42,0x7e};
+ BYTE hash2[MD5_BLOCK_SIZE] = {0x90,0x01,0x50,0x98,0x3c,0xd2,0x4f,0xb0,0xd6,0x96,0x3f,0x7d,0x28,0xe1,0x7f,0x72};
+ BYTE hash3[MD5_BLOCK_SIZE] = {0xd1,0x74,0xab,0x98,0xd2,0x77,0xd9,0xf5,0xa5,0x61,0x1c,0x2c,0x9f,0x41,0x9d,0x9f};
+ BYTE buf[16];
+ MD5_CTX ctx;
+ int pass = 1;
+
+ md5_init(&ctx);
+ md5_update(&ctx, text1, strlen(text1));
+ md5_final(&ctx, buf);
+ pass = pass && !memcmp(hash1, buf, MD5_BLOCK_SIZE);
+
+ // Note the MD5 object can be reused.
+ md5_init(&ctx);
+ md5_update(&ctx, text2, strlen(text2));
+ md5_final(&ctx, buf);
+ pass = pass && !memcmp(hash2, buf, MD5_BLOCK_SIZE);
+
+ // Note the data is being added in two chunks.
+ md5_init(&ctx);
+ md5_update(&ctx, text3_1, strlen(text3_1));
+ md5_update(&ctx, text3_2, strlen(text3_2));
+ md5_final(&ctx, buf);
+ pass = pass && !memcmp(hash3, buf, MD5_BLOCK_SIZE);
+
+ return(pass);
+}
+
+int main()
+{
+ printf("MD5 tests: %s\n", md5_test() ? "SUCCEEDED" : "FAILED");
+
+ return(0);
+}
--- /dev/null
+/*********************************************************************
+* Filename: rot-13.c
+* Author: Brad Conte (brad AT bradconte.com)
+* Copyright:
+* Disclaimer: This code is presented "as is" without any guarantees.
+* Details: Implementation of the ROT-13 encryption algorithm.
+ Algorithm specification can be found here:
+ *
+ This implementation uses little endian byte order.
+*********************************************************************/
+
+/*************************** HEADER FILES ***************************/
+#include <string.h>
+#include "rot-13.h"
+
+/*********************** FUNCTION DEFINITIONS ***********************/
+void rot13(char str[])
+{
+ int case_type, idx, len;
+
+ for (idx = 0, len = strlen(str); idx < len; idx++) {
+ // Only process alphabetic characters.
+ if (str[idx] < 'A' || (str[idx] > 'Z' && str[idx] < 'a') || str[idx] > 'z')
+ continue;
+ // Determine if the char is upper or lower case.
+ if (str[idx] >= 'a')
+ case_type = 'a';
+ else
+ case_type = 'A';
+ // Rotate the char's value, ensuring it doesn't accidentally "fall off" the end.
+ str[idx] = (str[idx] + 13) % (case_type + 26);
+ if (str[idx] < 26)
+ str[idx] += case_type;
+ }
+}
--- /dev/null
+/*********************************************************************
+* Filename: rot-13.h
+* Author: Brad Conte (brad AT bradconte.com)
+* Copyright:
+* Disclaimer: This code is presented "as is" without any guarantees.
+* Details: Defines the API for the corresponding ROT-13 implementation.
+*********************************************************************/
+
+#ifndef ROT13_H
+#define ROT13_H
+
+/*************************** HEADER FILES ***************************/
+#include <stddef.h>
+
+/*********************** FUNCTION DECLARATIONS **********************/
+// Performs IN PLACE rotation of the input. Assumes input is NULL terminated.
+// Preserves each charcter's case. Ignores non alphabetic characters.
+void rot13(char str[]);
+
+#endif // ROT13_H
--- /dev/null
+/*********************************************************************
+* Filename: rot-13_test.c
+* Author: Brad Conte (brad AT bradconte.com)
+* Copyright:
+* Disclaimer: This code is presented "as is" without any guarantees.
+* Details: Performs known-answer tests on the corresponding ROT-13
+ implementation. These tests do not encompass the full
+ range of available test vectors, however, if the tests
+ pass it is very, very likely that the code is correct
+ and was compiled properly. This code also serves as
+ example usage of the functions.
+*********************************************************************/
+
+/*************************** HEADER FILES ***************************/
+#include <stdio.h>
+#include <string.h>
+#include "rot-13.h"
+
+/*********************** FUNCTION DEFINITIONS ***********************/
+int rot13_test()
+{
+ char text[] = {"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"};
+ char code[] = {"NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm"};
+ char buf[1024];
+ int pass = 1;
+
+ // To encode, just apply ROT-13.
+ strcpy(buf, text);
+ rot13(buf);
+ pass = pass && !strcmp(code, buf);
+
+ // To decode, just re-apply ROT-13.
+ rot13(buf);
+ pass = pass && !strcmp(text, buf);
+
+ return(pass);
+}
+
+int main()
+{
+ printf("ROT-13 tests: %s\n", rot13_test() ? "SUCCEEDED" : "FAILED");
+
+ return(0);
+}
--- /dev/null
+/*********************************************************************
+* Filename: sha1.c
+* Author: Brad Conte (brad AT bradconte.com)
+* Copyright:
+* Disclaimer: This code is presented "as is" without any guarantees.
+* Details: Implementation of the SHA1 hashing algorithm.
+ Algorithm specification can be found here:
+ * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf
+ This implementation uses little endian byte order.
+*********************************************************************/
+
+/*************************** HEADER FILES ***************************/
+#include <stdlib.h>
+#include <memory.h>
+#include "sha1.h"
+
+/****************************** MACROS ******************************/
+#define ROTLEFT(a, b) ((a << b) | (a >> (32 - b)))
+
+/*********************** FUNCTION DEFINITIONS ***********************/
+void sha1_transform(SHA1_CTX *ctx, const BYTE data[])
+{
+ WORD a, b, c, d, e, i, j, t, m[80];
+
+ for (i = 0, j = 0; i < 16; ++i, j += 4)
+ m[i] = (data[j] << 24) + (data[j + 1] << 16) + (data[j + 2] << 8) + (data[j + 3]);
+ for ( ; i < 80; ++i) {
+ m[i] = (m[i - 3] ^ m[i - 8] ^ m[i - 14] ^ m[i - 16]);
+ m[i] = (m[i] << 1) | (m[i] >> 31);
+ }
+
+ a = ctx->state[0];
+ b = ctx->state[1];
+ c = ctx->state[2];
+ d = ctx->state[3];
+ e = ctx->state[4];
+
+ for (i = 0; i < 20; ++i) {
+ t = ROTLEFT(a, 5) + ((b & c) ^ (~b & d)) + e + ctx->k[0] + m[i];
+ e = d;
+ d = c;
+ c = ROTLEFT(b, 30);
+ b = a;
+ a = t;
+ }
+ for ( ; i < 40; ++i) {
+ t = ROTLEFT(a, 5) + (b ^ c ^ d) + e + ctx->k[1] + m[i];
+ e = d;
+ d = c;
+ c = ROTLEFT(b, 30);
+ b = a;
+ a = t;
+ }
+ for ( ; i < 60; ++i) {
+ t = ROTLEFT(a, 5) + ((b & c) ^ (b & d) ^ (c & d)) + e + ctx->k[2] + m[i];
+ e = d;
+ d = c;
+ c = ROTLEFT(b, 30);
+ b = a;
+ a = t;
+ }
+ for ( ; i < 80; ++i) {
+ t = ROTLEFT(a, 5) + (b ^ c ^ d) + e + ctx->k[3] + m[i];
+ e = d;
+ d = c;
+ c = ROTLEFT(b, 30);
+ b = a;
+ a = t;
+ }
+
+ ctx->state[0] += a;
+ ctx->state[1] += b;
+ ctx->state[2] += c;
+ ctx->state[3] += d;
+ ctx->state[4] += e;
+}
+
+void sha1_init(SHA1_CTX *ctx)
+{
+ ctx->datalen = 0;
+ ctx->bitlen = 0;
+ ctx->state[0] = 0x67452301;
+ ctx->state[1] = 0xEFCDAB89;
+ ctx->state[2] = 0x98BADCFE;
+ ctx->state[3] = 0x10325476;
+ ctx->state[4] = 0xc3d2e1f0;
+ ctx->k[0] = 0x5a827999;
+ ctx->k[1] = 0x6ed9eba1;
+ ctx->k[2] = 0x8f1bbcdc;
+ ctx->k[3] = 0xca62c1d6;
+}
+
+void sha1_update(SHA1_CTX *ctx, const BYTE data[], size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len; ++i) {
+ ctx->data[ctx->datalen] = data[i];
+ ctx->datalen++;
+ if (ctx->datalen == 64) {
+ sha1_transform(ctx, ctx->data);
+ ctx->bitlen += 512;
+ ctx->datalen = 0;
+ }
+ }
+}
+
+void sha1_final(SHA1_CTX *ctx, BYTE hash[])
+{
+ WORD i;
+
+ i = ctx->datalen;
+
+ // Pad whatever data is left in the buffer.
+ if (ctx->datalen < 56) {
+ ctx->data[i++] = 0x80;
+ while (i < 56)
+ ctx->data[i++] = 0x00;
+ }
+ else {
+ ctx->data[i++] = 0x80;
+ while (i < 64)
+ ctx->data[i++] = 0x00;
+ sha1_transform(ctx, ctx->data);
+ memset(ctx->data, 0, 56);
+ }
+
+ // Append to the padding the total message's length in bits and transform.
+ ctx->bitlen += ctx->datalen * 8;
+ ctx->data[63] = ctx->bitlen;
+ ctx->data[62] = ctx->bitlen >> 8;
+ ctx->data[61] = ctx->bitlen >> 16;
+ ctx->data[60] = ctx->bitlen >> 24;
+ ctx->data[59] = ctx->bitlen >> 32;
+ ctx->data[58] = ctx->bitlen >> 40;
+ ctx->data[57] = ctx->bitlen >> 48;
+ ctx->data[56] = ctx->bitlen >> 56;
+ sha1_transform(ctx, ctx->data);
+
+ // Since this implementation uses little endian byte ordering and MD uses big endian,
+ // reverse all the bytes when copying the final state to the output hash.
+ for (i = 0; i < 4; ++i) {
+ hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;
+ hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff;
+ hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff;
+ hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff;
+ hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff;
+ }
+}
--- /dev/null
+/*********************************************************************
+* Filename: sha1.h
+* Author: Brad Conte (brad AT bradconte.com)
+* Copyright:
+* Disclaimer: This code is presented "as is" without any guarantees.
+* Details: Defines the API for the corresponding SHA1 implementation.
+*********************************************************************/
+
+#ifndef SHA1_H
+#define SHA1_H
+
+/*************************** HEADER FILES ***************************/
+#include <stddef.h>
+
+/****************************** MACROS ******************************/
+#define SHA1_BLOCK_SIZE 20 // SHA1 outputs a 20 byte digest
+
+/**************************** DATA TYPES ****************************/
+typedef unsigned char BYTE; // 8-bit byte
+typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines
+
+typedef struct {
+ BYTE data[64];
+ WORD datalen;
+ unsigned long long bitlen;
+ WORD state[5];
+ WORD k[4];
+} SHA1_CTX;
+
+/*********************** FUNCTION DECLARATIONS **********************/
+void sha1_init(SHA1_CTX *ctx);
+void sha1_update(SHA1_CTX *ctx, const BYTE data[], size_t len);
+void sha1_final(SHA1_CTX *ctx, BYTE hash[]);
+
+#endif // SHA1_H
--- /dev/null
+/*********************************************************************
+* Filename: sha1_test.c
+* Author: Brad Conte (brad AT bradconte.com)
+* Copyright:
+* Disclaimer: This code is presented "as is" without any guarantees.
+* Details: Performs known-answer tests on the corresponding SHA1
+ implementation. These tests do not encompass the full
+ range of available test vectors, however, if the tests
+ pass it is very, very likely that the code is correct
+ and was compiled properly. This code also serves as
+ example usage of the functions.
+*********************************************************************/
+
+/*************************** HEADER FILES ***************************/
+#include <stdio.h>
+#include <memory.h>
+#include <string.h>
+#include "sha1.h"
+
+/*********************** FUNCTION DEFINITIONS ***********************/
+int sha1_test()
+{
+ BYTE text1[] = {"abc"};
+ BYTE text2[] = {"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"};
+ BYTE text3[] = {"aaaaaaaaaa"};
+ BYTE hash1[SHA1_BLOCK_SIZE] = {0xa9,0x99,0x3e,0x36,0x47,0x06,0x81,0x6a,0xba,0x3e,0x25,0x71,0x78,0x50,0xc2,0x6c,0x9c,0xd0,0xd8,0x9d};
+ BYTE hash2[SHA1_BLOCK_SIZE] = {0x84,0x98,0x3e,0x44,0x1c,0x3b,0xd2,0x6e,0xba,0xae,0x4a,0xa1,0xf9,0x51,0x29,0xe5,0xe5,0x46,0x70,0xf1};
+ BYTE hash3[SHA1_BLOCK_SIZE] = {0x34,0xaa,0x97,0x3c,0xd4,0xc4,0xda,0xa4,0xf6,0x1e,0xeb,0x2b,0xdb,0xad,0x27,0x31,0x65,0x34,0x01,0x6f};
+ BYTE buf[SHA1_BLOCK_SIZE];
+ int idx;
+ SHA1_CTX ctx;
+ int pass = 1;
+
+ sha1_init(&ctx);
+ sha1_update(&ctx, text1, strlen(text1));
+ sha1_final(&ctx, buf);
+ pass = pass && !memcmp(hash1, buf, SHA1_BLOCK_SIZE);
+
+ sha1_init(&ctx);
+ sha1_update(&ctx, text2, strlen(text2));
+ sha1_final(&ctx, buf);
+ pass = pass && !memcmp(hash2, buf, SHA1_BLOCK_SIZE);
+
+ sha1_init(&ctx);
+ for (idx = 0; idx < 100000; ++idx)
+ sha1_update(&ctx, text3, strlen(text3));
+ sha1_final(&ctx, buf);
+ pass = pass && !memcmp(hash3, buf, SHA1_BLOCK_SIZE);
+
+ return(pass);
+}
+
+int main()
+{
+ printf("SHA1 tests: %s\n", sha1_test() ? "SUCCEEDED" : "FAILED");
+
+ return(0);
+}
--- /dev/null
+/*********************************************************************
+* Filename: sha256.c
+* Author: Brad Conte (brad AT bradconte.com)
+* Copyright:
+* Disclaimer: This code is presented "as is" without any guarantees.
+* Details: Implementation of the SHA-256 hashing algorithm.
+ SHA-256 is one of the three algorithms in the SHA2
+ specification. The others, SHA-384 and SHA-512, are not
+ offered in this implementation.
+ Algorithm specification can be found here:
+ * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf
+ This implementation uses little endian byte order.
+*********************************************************************/
+
+/*************************** HEADER FILES ***************************/
+#include <stdlib.h>
+#include <memory.h>
+#include <string.h>
+#include "sha256.h"
+
+/****************************** MACROS ******************************/
+#define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b))))
+#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b))))
+
+#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z)))
+#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22))
+#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25))
+#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3))
+#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10))
+
+/**************************** VARIABLES *****************************/
+static const WORD k[64] = {
+ 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
+ 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
+ 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
+ 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
+ 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
+ 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
+ 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
+ 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
+};
+
+/*********************** FUNCTION DEFINITIONS ***********************/
+void sha256_transform(SHA256_CTX *ctx, const BYTE data[])
+{
+ WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];
+
+ for (i = 0, j = 0; i < 16; ++i, j += 4)
+ m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]);
+ for ( ; i < 64; ++i)
+ m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16];
+
+ a = ctx->state[0];
+ b = ctx->state[1];
+ c = ctx->state[2];
+ d = ctx->state[3];
+ e = ctx->state[4];
+ f = ctx->state[5];
+ g = ctx->state[6];
+ h = ctx->state[7];
+
+ for (i = 0; i < 64; ++i) {
+ t1 = h + EP1(e) + CH(e,f,g) + k[i] + m[i];
+ t2 = EP0(a) + MAJ(a,b,c);
+ h = g;
+ g = f;
+ f = e;
+ e = d + t1;
+ d = c;
+ c = b;
+ b = a;
+ a = t1 + t2;
+ }
+
+ ctx->state[0] += a;
+ ctx->state[1] += b;
+ ctx->state[2] += c;
+ ctx->state[3] += d;
+ ctx->state[4] += e;
+ ctx->state[5] += f;
+ ctx->state[6] += g;
+ ctx->state[7] += h;
+}
+
+void sha256_init(SHA256_CTX *ctx)
+{
+ ctx->datalen = 0;
+ ctx->bitlen = 0;
+ ctx->state[0] = 0x6a09e667;
+ ctx->state[1] = 0xbb67ae85;
+ ctx->state[2] = 0x3c6ef372;
+ ctx->state[3] = 0xa54ff53a;
+ ctx->state[4] = 0x510e527f;
+ ctx->state[5] = 0x9b05688c;
+ ctx->state[6] = 0x1f83d9ab;
+ ctx->state[7] = 0x5be0cd19;
+}
+
+void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len)
+{
+ WORD i;
+
+ for (i = 0; i < len; ++i) {
+ ctx->data[ctx->datalen] = data[i];
+ ctx->datalen++;
+ if (ctx->datalen == 64) {
+ sha256_transform(ctx, ctx->data);
+ ctx->bitlen += 512;
+ ctx->datalen = 0;
+ }
+ }
+}
+
+void sha256_final(SHA256_CTX *ctx, BYTE hash[])
+{
+ WORD i;
+
+ i = ctx->datalen;
+
+ // Pad whatever data is left in the buffer.
+ if (ctx->datalen < 56) {
+ ctx->data[i++] = 0x80;
+ while (i < 56)
+ ctx->data[i++] = 0x00;
+ }
+ else {
+ ctx->data[i++] = 0x80;
+ while (i < 64)
+ ctx->data[i++] = 0x00;
+ sha256_transform(ctx, ctx->data);
+ memset(ctx->data, 0, 56);
+ }
+
+ // Append to the padding the total message's length in bits and transform.
+ ctx->bitlen += ctx->datalen * 8;
+ ctx->data[63] = ctx->bitlen;
+ ctx->data[62] = ctx->bitlen >> 8;
+ ctx->data[61] = ctx->bitlen >> 16;
+ ctx->data[60] = ctx->bitlen >> 24;
+ ctx->data[59] = ctx->bitlen >> 32;
+ ctx->data[58] = ctx->bitlen >> 40;
+ ctx->data[57] = ctx->bitlen >> 48;
+ ctx->data[56] = ctx->bitlen >> 56;
+ sha256_transform(ctx, ctx->data);
+
+ // Since this implementation uses little endian byte ordering and SHA uses big endian,
+ // reverse all the bytes when copying the final state to the output hash.
+ for (i = 0; i < 4; ++i) {
+ hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;
+ hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff;
+ hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff;
+ hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff;
+ hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff;
+ hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff;
+ hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff;
+ hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff;
+ }
+}
--- /dev/null
+/*********************************************************************
+* Filename: sha256.h
+* Author: Brad Conte (brad AT bradconte.com)
+* Copyright:
+* Disclaimer: This code is presented "as is" without any guarantees.
+* Details: Defines the API for the corresponding SHA1 implementation.
+*********************************************************************/
+
+#ifndef SHA256_H
+#define SHA256_H
+
+/*************************** HEADER FILES ***************************/
+#include <stddef.h>
+
+/****************************** MACROS ******************************/
+#define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest
+
+/**************************** DATA TYPES ****************************/
+typedef unsigned char BYTE; // 8-bit byte
+typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines
+
+typedef struct {
+ BYTE data[64];
+ WORD datalen;
+ unsigned long long bitlen;
+ WORD state[8];
+} SHA256_CTX;
+
+/*********************** FUNCTION DECLARATIONS **********************/
+void sha256_init(SHA256_CTX *ctx);
+void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len);
+void sha256_final(SHA256_CTX *ctx, BYTE hash[]);
+
+#endif // SHA256_H
--- /dev/null
+/*********************************************************************
+* Filename: sha256.c
+* Author: Brad Conte (brad AT bradconte.com)
+* Copyright:
+* Disclaimer: This code is presented "as is" without any guarantees.
+* Details: Performs known-answer tests on the corresponding SHA1
+ implementation. These tests do not encompass the full
+ range of available test vectors, however, if the tests
+ pass it is very, very likely that the code is correct
+ and was compiled properly. This code also serves as
+ example usage of the functions.
+*********************************************************************/
+
+/*************************** HEADER FILES ***************************/
+#include <stdio.h>
+#include <memory.h>
+#include <string.h>
+#include "sha256.h"
+
+/*********************** FUNCTION DEFINITIONS ***********************/
+int sha256_test()
+{
+ BYTE text1[] = {"abc"};
+ BYTE text2[] = {"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"};
+ BYTE text3[] = {"aaaaaaaaaa"};
+ BYTE hash1[SHA256_BLOCK_SIZE] = {0xba,0x78,0x16,0xbf,0x8f,0x01,0xcf,0xea,0x41,0x41,0x40,0xde,0x5d,0xae,0x22,0x23,
+ 0xb0,0x03,0x61,0xa3,0x96,0x17,0x7a,0x9c,0xb4,0x10,0xff,0x61,0xf2,0x00,0x15,0xad};
+ BYTE hash2[SHA256_BLOCK_SIZE] = {0x24,0x8d,0x6a,0x61,0xd2,0x06,0x38,0xb8,0xe5,0xc0,0x26,0x93,0x0c,0x3e,0x60,0x39,
+ 0xa3,0x3c,0xe4,0x59,0x64,0xff,0x21,0x67,0xf6,0xec,0xed,0xd4,0x19,0xdb,0x06,0xc1};
+ BYTE hash3[SHA256_BLOCK_SIZE] = {0xcd,0xc7,0x6e,0x5c,0x99,0x14,0xfb,0x92,0x81,0xa1,0xc7,0xe2,0x84,0xd7,0x3e,0x67,
+ 0xf1,0x80,0x9a,0x48,0xa4,0x97,0x20,0x0e,0x04,0x6d,0x39,0xcc,0xc7,0x11,0x2c,0xd0};
+ BYTE buf[SHA256_BLOCK_SIZE];
+ SHA256_CTX ctx;
+ int idx;
+ int pass = 1;
+
+ sha256_init(&ctx);
+ sha256_update(&ctx, text1, strlen(text1));
+ sha256_final(&ctx, buf);
+ pass = pass && !memcmp(hash1, buf, SHA256_BLOCK_SIZE);
+
+ sha256_init(&ctx);
+ sha256_update(&ctx, text2, strlen(text2));
+ sha256_final(&ctx, buf);
+ pass = pass && !memcmp(hash2, buf, SHA256_BLOCK_SIZE);
+
+ sha256_init(&ctx);
+ for (idx = 0; idx < 100000; ++idx)
+ sha256_update(&ctx, text3, strlen(text3));
+ sha256_final(&ctx, buf);
+ pass = pass && !memcmp(hash3, buf, SHA256_BLOCK_SIZE);
+
+ return(pass);
+}
+
+int main()
+{
+ printf("SHA-256 tests: %s\n", sha256_test() ? "SUCCEEDED" : "FAILEd");
+
+ return(0);
+}
--- /dev/null
+/* header file for the curve25519-donna implementation, because the
+ * authors of that project don't supply one.
+ */
+#ifndef CURVE25519_DONNA_H
+#define CURVE25519_DONNA_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int curve25519_donna(unsigned char *output, const unsigned char *a,
+ const unsigned char *b);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/curve25519-donna-c64.a
+/curve25519-donna.a
+/test-curve25519-donna
+/speed-curve25519-donna
+/test-curve25519-donna-c64
+/speed-curve25519-donna-c64
+/test-sc-curve25519-donna-c64
+/build
+*.o
+*.pyc
+/dist
+/MANIFEST
--- /dev/null
+Copyright 2008, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+* Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+curve25519-donna: Curve25519 elliptic curve, public key function
+
+http://code.google.com/p/curve25519-donna/
+
+Adam Langley <agl@imperialviolet.org>
+
+Derived from public domain C code by Daniel J. Bernstein <djb@cr.yp.to>
+
+More information about curve25519 can be found here
+ http://cr.yp.to/ecdh.html
+
+djb's sample implementation of curve25519 is written in a special assembly
+language called qhasm and uses the floating point registers.
+
+This is, almost, a clean room reimplementation from the curve25519 paper. It
+uses many of the tricks described therein. Only the crecip function is taken
+from the sample implementation.
--- /dev/null
+CFLAGS=-Wmissing-prototypes -Wdeclaration-after-statement -O2 -Wall
+CFLAGS_32=-m32
+
+targets: curve25519-donna.a curve25519-donna-c64.a
+
+test: test-donna test-donna-c64
+
+clean:
+ rm -f *.o *.a *.pp test-curve25519-donna test-curve25519-donna-c64 speed-curve25519-donna speed-curve25519-donna-c64 test-noncanon-curve25519-donna test-noncanon-curve25519-donna-c64
+
+curve25519-donna.a: curve25519-donna.o
+ ar -rc curve25519-donna.a curve25519-donna.o
+ ranlib curve25519-donna.a
+
+curve25519-donna.o: curve25519-donna.c
+ gcc -c curve25519-donna.c $(CFLAGS) $(CFLAGS_32)
+
+curve25519-donna-c64.a: curve25519-donna-c64.o
+ ar -rc curve25519-donna-c64.a curve25519-donna-c64.o
+ ranlib curve25519-donna-c64.a
+
+curve25519-donna-c64.o: curve25519-donna-c64.c
+ gcc -c curve25519-donna-c64.c $(CFLAGS)
+
+test-donna: test-curve25519-donna
+ ./test-curve25519-donna | head -123456 | tail -1
+
+test-donna-c64: test-curve25519-donna-c64
+ ./test-curve25519-donna-c64 | head -123456 | tail -1
+
+test-curve25519-donna: test-curve25519.c curve25519-donna.a
+ gcc -o test-curve25519-donna test-curve25519.c curve25519-donna.a $(CFLAGS) $(CFLAGS_32)
+
+test-curve25519-donna-c64: test-curve25519.c curve25519-donna-c64.a
+ gcc -o test-curve25519-donna-c64 test-curve25519.c curve25519-donna-c64.a $(CFLAGS)
+
+speed-curve25519-donna: speed-curve25519.c curve25519-donna.a
+ gcc -o speed-curve25519-donna speed-curve25519.c curve25519-donna.a $(CFLAGS) $(CFLAGS_32)
+
+speed-curve25519-donna-c64: speed-curve25519.c curve25519-donna-c64.a
+ gcc -o speed-curve25519-donna-c64 speed-curve25519.c curve25519-donna-c64.a $(CFLAGS)
+
+test-sc-curve25519-donna-c64: test-sc-curve25519.c curve25519-donna-c64.a
+ gcc -o test-sc-curve25519-donna-c64 -O test-sc-curve25519.c curve25519-donna-c64.a test-sc-curve25519.s $(CFLAGS)
+
+test-noncanon-donna: test-noncanon-curve25519-donna
+ ./test-noncanon-curve25519-donna
+
+test-noncanon-donna-c64: test-noncanon-curve25519-donna-c64
+ ./test-noncanon-curve25519-donna-c64
+
+test-noncanon-curve25519-donna: test-noncanon.c curve25519-donna.a
+ gcc -o test-noncanon-curve25519-donna test-noncanon.c curve25519-donna.a $(CFLAGS) $(CFLAGS_32)
+
+test-noncanon-curve25519-donna-c64: test-noncanon.c curve25519-donna-c64.a
+ gcc -o test-noncanon-curve25519-donna-c64 test-noncanon.c curve25519-donna-c64.a $(CFLAGS)
--- /dev/null
+See http://code.google.com/p/curve25519-donna/ for details.
+
+BUILDING:
+
+If you run `make`, two .a archives will be built, similar to djb's curve25519
+code. Alternatively, read on:
+
+The C implementation is contained within curve25519-donna.c. It has no external
+dependancies and is BSD licenced. You can copy/include/link it directly in with
+your program. Recommended C flags: -O2
+
+The x86-64 bit implementation is contained within curve25519-donna-x86-64.c and
+curve25519-donna-x86-64.s. Build like this:
+
+% cpp curve25519-donna-x86-64.s > curve25519-donna-x86-64.s.pp
+% as -o curve25519-donna-x86-64.s.o curve25519-donna-x86-64.s.pp
+% gcc -O2 -c curve25519-donna-x86-64.c
+
+Then the two .o files can be linked in
+
+USAGE:
+
+The usage is exactly the same as djb's code (as described at
+http://cr.yp.to/ecdh.html) expect that the function is called curve25519_donna.
+
+In short,
+
+To generate a private key just generate 32 random bytes.
+
+To generate the public key, just do:
+
+ static const uint8_t basepoint[32] = {9};
+ curve25519_donna(mypublic, mysecret, basepoint);
+
+To generate an agreed key do:
+
+ uint8_t shared_key[32];
+ curve25519_donna(shared_key, mysecret, theirpublic);
+
+And hash the shared_key with a cryptographic hash function before using.
--- /dev/null
+/*
+ James Robson
+ Public domain.
+*/
+
+#include "Curve25519Donna.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+extern void curve25519_donna(unsigned char *output, const unsigned char *a,
+ const unsigned char *b);
+
+unsigned char*
+as_unsigned_char_array(JNIEnv* env, jbyteArray array, int* len);
+
+jbyteArray as_byte_array(JNIEnv* env, unsigned char* buf, int len);
+
+
+jbyteArray as_byte_array(JNIEnv* env, unsigned char* buf, int len) {
+ jbyteArray array = (*env)->NewByteArray(env, len);
+ (*env)->SetByteArrayRegion(env, array, 0, len, (jbyte*)buf);
+
+ //int i;
+ //for (i = 0;i < len;++i) printf("%02x",(unsigned int) buf[i]); printf(" ");
+ //printf("\n");
+
+ return array;
+}
+
+unsigned char*
+as_unsigned_char_array(JNIEnv* env, jbyteArray array, int* len) {
+
+ *len = (*env)->GetArrayLength(env, array);
+ unsigned char* buf = (unsigned char*)calloc(*len+1, sizeof(char));
+ (*env)->GetByteArrayRegion (env, array, 0, *len, (jbyte*)buf);
+ return buf;
+
+}
+
+JNIEXPORT jbyteArray JNICALL Java_Curve25519Donna_curve25519Donna
+ (JNIEnv *env, jobject obj, jbyteArray a, jbyteArray b) {
+
+ unsigned char o[32] = {0};
+ int l1, l2;
+ unsigned char* a1 = as_unsigned_char_array(env, a, &l1);
+ unsigned char* b1 = as_unsigned_char_array(env, b, &l2);
+
+ if ( !(l1 == 32 && l2 == 32) ) {
+ fprintf(stderr, "Error, must be length 32");
+ return NULL;
+ }
+
+
+ curve25519_donna(o, (const unsigned char*)a1, (const unsigned char*)b1);
+
+ free(a1);
+ free(b1);
+
+ return as_byte_array(env, (unsigned char*)o, 32);
+}
+
+JNIEXPORT jbyteArray JNICALL Java_Curve25519Donna_makePrivate
+ (JNIEnv *env, jobject obj, jbyteArray secret) {
+
+ int len;
+ unsigned char* k = as_unsigned_char_array(env, secret, &len);
+
+ if (len != 32) {
+ fprintf(stderr, "Error, must be length 32");
+ return NULL;
+ }
+
+ k[0] &= 248;
+ k[31] &= 127;
+ k[31] |= 64;
+ return as_byte_array(env, k, 32);
+}
+
+JNIEXPORT jbyteArray JNICALL Java_Curve25519Donna_getPublic
+ (JNIEnv *env, jobject obj, jbyteArray privkey) {
+
+ int len;
+ unsigned char* private = as_unsigned_char_array(env, privkey, &len);
+
+ if (len != 32) {
+ fprintf(stderr, "Error, must be length 32");
+ return NULL;
+ }
+
+ unsigned char pubkey[32];
+ unsigned char basepoint[32] = {9};
+
+ curve25519_donna(pubkey, private, basepoint);
+ return as_byte_array(env, (unsigned char*)pubkey, 32);
+}
+
+JNIEXPORT jbyteArray JNICALL Java_Curve25519Donna_makeSharedSecret
+ (JNIEnv *env, jobject obj, jbyteArray privkey, jbyteArray their_pubkey) {
+
+ unsigned char shared_secret[32];
+
+ int l1, l2;
+ unsigned char* private = as_unsigned_char_array(env, privkey, &l1);
+ unsigned char* pubkey = as_unsigned_char_array(env, their_pubkey, &l2);
+
+ if ( !(l1 == 32 && l2 == 32) ) {
+ fprintf(stderr, "Error, must be length 32");
+ return NULL;
+ }
+
+ curve25519_donna(shared_secret, private, pubkey);
+ return as_byte_array(env, (unsigned char*)shared_secret, 32);
+}
+
+JNIEXPORT void JNICALL Java_Curve25519Donna_helowrld
+ (JNIEnv *env, jobject obj) {
+ printf("helowrld\n");
+}
--- /dev/null
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class Curve25519Donna */
+
+#ifndef _Included_Curve25519Donna
+#define _Included_Curve25519Donna
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: Curve25519Donna
+ * Method: curve25519Donna
+ * Signature: ([B[B)[B
+ */
+JNIEXPORT jbyteArray JNICALL Java_Curve25519Donna_curve25519Donna
+ (JNIEnv *, jobject, jbyteArray, jbyteArray);
+
+/*
+ * Class: Curve25519Donna
+ * Method: makePrivate
+ * Signature: ([B)[B
+ */
+JNIEXPORT jbyteArray JNICALL Java_Curve25519Donna_makePrivate
+ (JNIEnv *, jobject, jbyteArray);
+
+/*
+ * Class: Curve25519Donna
+ * Method: getPublic
+ * Signature: ([B)[B
+ */
+JNIEXPORT jbyteArray JNICALL Java_Curve25519Donna_getPublic
+ (JNIEnv *, jobject, jbyteArray);
+
+/*
+ * Class: Curve25519Donna
+ * Method: makeSharedSecret
+ * Signature: ([B[B)[B
+ */
+JNIEXPORT jbyteArray JNICALL Java_Curve25519Donna_makeSharedSecret
+ (JNIEnv *, jobject, jbyteArray, jbyteArray);
+
+/*
+ * Class: Curve25519Donna
+ * Method: helowrld
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_Curve25519Donna_helowrld
+ (JNIEnv *, jobject);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
--- /dev/null
+/*
+ James Robson
+ Public domain.
+*/
+
+public class Curve25519Donna {
+
+ final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
+
+ public static String bytesToHex(byte[] bytes) {
+ char[] hexChars = new char[bytes.length * 2];
+ int v;
+ for ( int j = 0; j < bytes.length; j++ ) {
+ v = bytes[j] & 0xFF;
+ hexChars[j * 2] = hexArray[v >>> 4];
+ hexChars[j * 2 + 1] = hexArray[v & 0x0F];
+ }
+ return new String(hexChars);
+ }
+
+ public native byte[] curve25519Donna(byte[] a, byte[] b);
+ public native byte[] makePrivate(byte[] secret);
+ public native byte[] getPublic(byte[] privkey);
+ public native byte[] makeSharedSecret(byte[] privkey, byte[] theirPubKey);
+ public native void helowrld();
+
+ // Uncomment if your Java is 32-bit:
+ //static { System.loadLibrary("Curve25519Donna"); }
+
+ // Otherwise, load this 64-bit .jnilib:
+ static { System.loadLibrary("Curve25519Donna_64"); }
+
+ /*
+ To give the old tires a kick (OSX):
+ java -cp `pwd` Curve25519Donna
+ */
+ public static void main (String[] args) {
+
+ Curve25519Donna c = new Curve25519Donna();
+
+ // These should be 32 bytes long
+ byte[] user1Secret = "abcdefghijklmnopqrstuvwxyz123456".getBytes();
+ byte[] user2Secret = "654321zyxwvutsrqponmlkjihgfedcba".getBytes();
+
+
+ // You can use the curve function directly...
+
+ //byte[] o = c.curve25519Donna(a, b);
+ //System.out.println("o = " + bytesToHex(o));
+
+
+ // ... but it's not really necessary. Just use the following
+ // convenience methods:
+
+ byte[] privKey = c.makePrivate(user1Secret);
+ byte[] pubKey = c.getPublic(privKey);
+
+ byte[] privKey2 = c.makePrivate(user2Secret);
+ byte[] pubKey2 = c.getPublic(privKey2);
+
+ System.out.println("'user1' privKey = " + bytesToHex(privKey));
+ System.out.println("'user1' pubKey = " + bytesToHex(pubKey));
+ System.out.println("===================================================");
+
+ System.out.println("'user2' privKey = " + bytesToHex(privKey2));
+ System.out.println("'user2' pubKey = " + bytesToHex(pubKey2));
+ System.out.println("===================================================");
+
+
+ byte[] ss1 = c.makeSharedSecret(privKey, pubKey2);
+ System.out.println("'user1' computes shared secret: " + bytesToHex(ss1));
+
+ byte[] ss2 = c.makeSharedSecret(privKey2, pubKey);
+ System.out.println("'user2' computes shared secret: " + bytesToHex(ss2));
+
+ }
+}
--- /dev/null
+CFLAGS=-Wmissing-prototypes -Wdeclaration-after-statement -O2 -Wall
+CC=clang
+
+
+targets: curve25519-donna.a curve25519-donna-c64.a
+
+test: test-donna test-donna-c64
+
+
+clean:
+ rm -f java-src/*.class java-src/*.jnilib *.dylib *.o *.a *.pp test-curve25519-donna test-curve25519-donna-c64 speed-curve25519-donna speed-curve25519-donna-c64
+
+curve25519-donna.a: curve25519-donna.o
+ ar -rc curve25519-donna.a curve25519-donna.o
+ ranlib curve25519-donna.a
+
+
+##### OSX dynamic library (32- & 64-bit)
+
+curve25519donna.dylib: curve25519-donna.a curve25519-donna-c64.a
+ $(CC) -m32 -fpic -shared -Wl,-all_load curve25519-donna.a -Wl,-all_load -o libcurve25519donna.dylib
+ $(CC) -fpic -shared -Wl,-all_load curve25519-donna-c64.a -Wl,-all_load -o libcurve25519donna_64.dylib
+
+##### OSX/Java section hence
+
+# Java JNI - compiled for OSX (32- & 64-bit)
+Curve25519Donna.class:
+ cd java-src; javah -jni Curve25519Donna; cd ..
+ cd java-src; javac Curve25519Donna.java; cd ..
+
+Curve25519Donna.jnilib: curve25519-donna.a curve25519-donna-c64.a Curve25519Donna.class
+ @echo "Building 32-bit..."
+ clang -o java-src/libCurve25519Donna.jnilib $(CFLAGS) -lc -shared -m32 -I /System/Library/Frameworks/JavaVM.framework/Headers curve25519-donna.o java-src/Curve25519Donna.c
+ @echo "Building 64-bit..."
+ clang -o java-src/libCurve25519Donna_64.jnilib $(CFLAGS) -lc -shared -I /System/Library/Frameworks/JavaVM.framework/Headers curve25519-donna-c64.o java-src/Curve25519Donna.c
+
+##### OSX/Java section end
+
+curve25519-donna.o: curve25519-donna.c
+ $(CC) -c curve25519-donna.c $(CFLAGS) -m32
+
+curve25519-donna-c64.a: curve25519-donna-c64.o
+ ar -rc curve25519-donna-c64.a curve25519-donna-c64.o
+ ranlib curve25519-donna-c64.a
+
+curve25519-donna-c64.o: curve25519-donna-c64.c
+ $(CC) -c curve25519-donna-c64.c $(CFLAGS)
+
+test-donna: test-curve25519-donna
+ ./test-curve25519-donna | head -123456 | tail -1
+
+test-donna-c64: test-curve25519-donna-c64
+ ./test-curve25519-donna-c64 | head -123456 | tail -1
+
+test-curve25519-donna: test-curve25519.c curve25519-donna.a
+ $(CC) -o test-curve25519-donna test-curve25519.c curve25519-donna.a $(CFLAGS) -m32
+
+test-curve25519-donna-c64: test-curve25519.c curve25519-donna-c64.a
+ $(CC) -o test-curve25519-donna-c64 test-curve25519.c curve25519-donna-c64.a $(CFLAGS)
+
+speed-curve25519-donna: speed-curve25519.c curve25519-donna.a
+ $(CC) -o speed-curve25519-donna speed-curve25519.c curve25519-donna.a $(CFLAGS) -m32
+
+speed-curve25519-donna-c64: speed-curve25519.c curve25519-donna-c64.a
+ $(CC) -o speed-curve25519-donna-c64 speed-curve25519.c curve25519-donna-c64.a $(CFLAGS)
+
+test-sc-curve25519-donna-c64: test-sc-curve25519.c curve25519-donna-c64.a
+ $(CC) -o test-sc-curve25519-donna-c64 -O test-sc-curve25519.c curve25519-donna-c64.a test-sc-curve25519.s $(CFLAGS)
--- /dev/null
+/* Copyright 2008, Google Inc.
+ * All rights reserved.
+ *
+ * Code released into the public domain.
+ *
+ * curve25519-donna: Curve25519 elliptic curve, public key function
+ *
+ * http://code.google.com/p/curve25519-donna/
+ *
+ * Adam Langley <agl@imperialviolet.org>
+ *
+ * Derived from public domain C code by Daniel J. Bernstein <djb@cr.yp.to>
+ *
+ * More information about curve25519 can be found here
+ * http://cr.yp.to/ecdh.html
+ *
+ * djb's sample implementation of curve25519 is written in a special assembly
+ * language called qhasm and uses the floating point registers.
+ *
+ * This is, almost, a clean room reimplementation from the curve25519 paper. It
+ * uses many of the tricks described therein. Only the crecip function is taken
+ * from the sample implementation.
+ */
+
+#include <string.h>
+#include <stdint.h>
+
+typedef uint8_t u8;
+typedef uint64_t limb;
+typedef limb felem[5];
+// This is a special gcc mode for 128-bit integers. It's implemented on 64-bit
+// platforms only as far as I know.
+typedef unsigned uint128_t __attribute__((mode(TI)));
+
+#undef force_inline
+#define force_inline __attribute__((always_inline))
+
+/* Sum two numbers: output += in */
+static inline void force_inline
+fsum(limb *output, const limb *in) {
+ output[0] += in[0];
+ output[1] += in[1];
+ output[2] += in[2];
+ output[3] += in[3];
+ output[4] += in[4];
+}
+
+/* Find the difference of two numbers: output = in - output
+ * (note the order of the arguments!)
+ *
+ * Assumes that out[i] < 2**52
+ * On return, out[i] < 2**55
+ */
+static inline void force_inline
+fdifference_backwards(felem out, const felem in) {
+ /* 152 is 19 << 3 */
+ static const limb two54m152 = (((limb)1) << 54) - 152;
+ static const limb two54m8 = (((limb)1) << 54) - 8;
+
+ out[0] = in[0] + two54m152 - out[0];
+ out[1] = in[1] + two54m8 - out[1];
+ out[2] = in[2] + two54m8 - out[2];
+ out[3] = in[3] + two54m8 - out[3];
+ out[4] = in[4] + two54m8 - out[4];
+}
+
+/* Multiply a number by a scalar: output = in * scalar */
+static inline void force_inline
+fscalar_product(felem output, const felem in, const limb scalar) {
+ uint128_t a;
+
+ a = ((uint128_t) in[0]) * scalar;
+ output[0] = ((limb)a) & 0x7ffffffffffff;
+
+ a = ((uint128_t) in[1]) * scalar + ((limb) (a >> 51));
+ output[1] = ((limb)a) & 0x7ffffffffffff;
+
+ a = ((uint128_t) in[2]) * scalar + ((limb) (a >> 51));
+ output[2] = ((limb)a) & 0x7ffffffffffff;
+
+ a = ((uint128_t) in[3]) * scalar + ((limb) (a >> 51));
+ output[3] = ((limb)a) & 0x7ffffffffffff;
+
+ a = ((uint128_t) in[4]) * scalar + ((limb) (a >> 51));
+ output[4] = ((limb)a) & 0x7ffffffffffff;
+
+ output[0] += (a >> 51) * 19;
+}
+
+/* Multiply two numbers: output = in2 * in
+ *
+ * output must be distinct to both inputs. The inputs are reduced coefficient
+ * form, the output is not.
+ *
+ * Assumes that in[i] < 2**55 and likewise for in2.
+ * On return, output[i] < 2**52
+ */
+static inline void force_inline
+fmul(felem output, const felem in2, const felem in) {
+ uint128_t t[5];
+ limb r0,r1,r2,r3,r4,s0,s1,s2,s3,s4,c;
+
+ r0 = in[0];
+ r1 = in[1];
+ r2 = in[2];
+ r3 = in[3];
+ r4 = in[4];
+
+ s0 = in2[0];
+ s1 = in2[1];
+ s2 = in2[2];
+ s3 = in2[3];
+ s4 = in2[4];
+
+ t[0] = ((uint128_t) r0) * s0;
+ t[1] = ((uint128_t) r0) * s1 + ((uint128_t) r1) * s0;
+ t[2] = ((uint128_t) r0) * s2 + ((uint128_t) r2) * s0 + ((uint128_t) r1) * s1;
+ t[3] = ((uint128_t) r0) * s3 + ((uint128_t) r3) * s0 + ((uint128_t) r1) * s2 + ((uint128_t) r2) * s1;
+ t[4] = ((uint128_t) r0) * s4 + ((uint128_t) r4) * s0 + ((uint128_t) r3) * s1 + ((uint128_t) r1) * s3 + ((uint128_t) r2) * s2;
+
+ r4 *= 19;
+ r1 *= 19;
+ r2 *= 19;
+ r3 *= 19;
+
+ t[0] += ((uint128_t) r4) * s1 + ((uint128_t) r1) * s4 + ((uint128_t) r2) * s3 + ((uint128_t) r3) * s2;
+ t[1] += ((uint128_t) r4) * s2 + ((uint128_t) r2) * s4 + ((uint128_t) r3) * s3;
+ t[2] += ((uint128_t) r4) * s3 + ((uint128_t) r3) * s4;
+ t[3] += ((uint128_t) r4) * s4;
+
+ r0 = (limb)t[0] & 0x7ffffffffffff; c = (limb)(t[0] >> 51);
+ t[1] += c; r1 = (limb)t[1] & 0x7ffffffffffff; c = (limb)(t[1] >> 51);
+ t[2] += c; r2 = (limb)t[2] & 0x7ffffffffffff; c = (limb)(t[2] >> 51);
+ t[3] += c; r3 = (limb)t[3] & 0x7ffffffffffff; c = (limb)(t[3] >> 51);
+ t[4] += c; r4 = (limb)t[4] & 0x7ffffffffffff; c = (limb)(t[4] >> 51);
+ r0 += c * 19; c = r0 >> 51; r0 = r0 & 0x7ffffffffffff;
+ r1 += c; c = r1 >> 51; r1 = r1 & 0x7ffffffffffff;
+ r2 += c;
+
+ output[0] = r0;
+ output[1] = r1;
+ output[2] = r2;
+ output[3] = r3;
+ output[4] = r4;
+}
+
+static inline void force_inline
+fsquare_times(felem output, const felem in, limb count) {
+ uint128_t t[5];
+ limb r0,r1,r2,r3,r4,c;
+ limb d0,d1,d2,d4,d419;
+
+ r0 = in[0];
+ r1 = in[1];
+ r2 = in[2];
+ r3 = in[3];
+ r4 = in[4];
+
+ do {
+ d0 = r0 * 2;
+ d1 = r1 * 2;
+ d2 = r2 * 2 * 19;
+ d419 = r4 * 19;
+ d4 = d419 * 2;
+
+ t[0] = ((uint128_t) r0) * r0 + ((uint128_t) d4) * r1 + (((uint128_t) d2) * (r3 ));
+ t[1] = ((uint128_t) d0) * r1 + ((uint128_t) d4) * r2 + (((uint128_t) r3) * (r3 * 19));
+ t[2] = ((uint128_t) d0) * r2 + ((uint128_t) r1) * r1 + (((uint128_t) d4) * (r3 ));
+ t[3] = ((uint128_t) d0) * r3 + ((uint128_t) d1) * r2 + (((uint128_t) r4) * (d419 ));
+ t[4] = ((uint128_t) d0) * r4 + ((uint128_t) d1) * r3 + (((uint128_t) r2) * (r2 ));
+
+ r0 = (limb)t[0] & 0x7ffffffffffff; c = (limb)(t[0] >> 51);
+ t[1] += c; r1 = (limb)t[1] & 0x7ffffffffffff; c = (limb)(t[1] >> 51);
+ t[2] += c; r2 = (limb)t[2] & 0x7ffffffffffff; c = (limb)(t[2] >> 51);
+ t[3] += c; r3 = (limb)t[3] & 0x7ffffffffffff; c = (limb)(t[3] >> 51);
+ t[4] += c; r4 = (limb)t[4] & 0x7ffffffffffff; c = (limb)(t[4] >> 51);
+ r0 += c * 19; c = r0 >> 51; r0 = r0 & 0x7ffffffffffff;
+ r1 += c; c = r1 >> 51; r1 = r1 & 0x7ffffffffffff;
+ r2 += c;
+ } while(--count);
+
+ output[0] = r0;
+ output[1] = r1;
+ output[2] = r2;
+ output[3] = r3;
+ output[4] = r4;
+}
+
+/* Load a little-endian 64-bit number */
+static limb
+load_limb(const u8 *in) {
+ return
+ ((limb)in[0]) |
+ (((limb)in[1]) << 8) |
+ (((limb)in[2]) << 16) |
+ (((limb)in[3]) << 24) |
+ (((limb)in[4]) << 32) |
+ (((limb)in[5]) << 40) |
+ (((limb)in[6]) << 48) |
+ (((limb)in[7]) << 56);
+}
+
+static void
+store_limb(u8 *out, limb in) {
+ out[0] = in & 0xff;
+ out[1] = (in >> 8) & 0xff;
+ out[2] = (in >> 16) & 0xff;
+ out[3] = (in >> 24) & 0xff;
+ out[4] = (in >> 32) & 0xff;
+ out[5] = (in >> 40) & 0xff;
+ out[6] = (in >> 48) & 0xff;
+ out[7] = (in >> 56) & 0xff;
+}
+
+/* Take a little-endian, 32-byte number and expand it into polynomial form */
+static void
+fexpand(limb *output, const u8 *in) {
+ output[0] = load_limb(in) & 0x7ffffffffffff;
+ output[1] = (load_limb(in+6) >> 3) & 0x7ffffffffffff;
+ output[2] = (load_limb(in+12) >> 6) & 0x7ffffffffffff;
+ output[3] = (load_limb(in+19) >> 1) & 0x7ffffffffffff;
+ output[4] = (load_limb(in+24) >> 12) & 0x7ffffffffffff;
+}
+
+/* Take a fully reduced polynomial form number and contract it into a
+ * little-endian, 32-byte array
+ */
+static void
+fcontract(u8 *output, const felem input) {
+ uint128_t t[5];
+
+ t[0] = input[0];
+ t[1] = input[1];
+ t[2] = input[2];
+ t[3] = input[3];
+ t[4] = input[4];
+
+ t[1] += t[0] >> 51; t[0] &= 0x7ffffffffffff;
+ t[2] += t[1] >> 51; t[1] &= 0x7ffffffffffff;
+ t[3] += t[2] >> 51; t[2] &= 0x7ffffffffffff;
+ t[4] += t[3] >> 51; t[3] &= 0x7ffffffffffff;
+ t[0] += 19 * (t[4] >> 51); t[4] &= 0x7ffffffffffff;
+
+ t[1] += t[0] >> 51; t[0] &= 0x7ffffffffffff;
+ t[2] += t[1] >> 51; t[1] &= 0x7ffffffffffff;
+ t[3] += t[2] >> 51; t[2] &= 0x7ffffffffffff;
+ t[4] += t[3] >> 51; t[3] &= 0x7ffffffffffff;
+ t[0] += 19 * (t[4] >> 51); t[4] &= 0x7ffffffffffff;
+
+ /* now t is between 0 and 2^255-1, properly carried. */
+ /* case 1: between 0 and 2^255-20. case 2: between 2^255-19 and 2^255-1. */
+
+ t[0] += 19;
+
+ t[1] += t[0] >> 51; t[0] &= 0x7ffffffffffff;
+ t[2] += t[1] >> 51; t[1] &= 0x7ffffffffffff;
+ t[3] += t[2] >> 51; t[2] &= 0x7ffffffffffff;
+ t[4] += t[3] >> 51; t[3] &= 0x7ffffffffffff;
+ t[0] += 19 * (t[4] >> 51); t[4] &= 0x7ffffffffffff;
+
+ /* now between 19 and 2^255-1 in both cases, and offset by 19. */
+
+ t[0] += 0x8000000000000 - 19;
+ t[1] += 0x8000000000000 - 1;
+ t[2] += 0x8000000000000 - 1;
+ t[3] += 0x8000000000000 - 1;
+ t[4] += 0x8000000000000 - 1;
+
+ /* now between 2^255 and 2^256-20, and offset by 2^255. */
+
+ t[1] += t[0] >> 51; t[0] &= 0x7ffffffffffff;
+ t[2] += t[1] >> 51; t[1] &= 0x7ffffffffffff;
+ t[3] += t[2] >> 51; t[2] &= 0x7ffffffffffff;
+ t[4] += t[3] >> 51; t[3] &= 0x7ffffffffffff;
+ t[4] &= 0x7ffffffffffff;
+
+ store_limb(output, t[0] | (t[1] << 51));
+ store_limb(output+8, (t[1] >> 13) | (t[2] << 38));
+ store_limb(output+16, (t[2] >> 26) | (t[3] << 25));
+ store_limb(output+24, (t[3] >> 39) | (t[4] << 12));
+}
+
+/* Input: Q, Q', Q-Q'
+ * Output: 2Q, Q+Q'
+ *
+ * x2 z3: long form
+ * x3 z3: long form
+ * x z: short form, destroyed
+ * xprime zprime: short form, destroyed
+ * qmqp: short form, preserved
+ */
+static void
+fmonty(limb *x2, limb *z2, /* output 2Q */
+ limb *x3, limb *z3, /* output Q + Q' */
+ limb *x, limb *z, /* input Q */
+ limb *xprime, limb *zprime, /* input Q' */
+ const limb *qmqp /* input Q - Q' */) {
+ limb origx[5], origxprime[5], zzz[5], xx[5], zz[5], xxprime[5],
+ zzprime[5], zzzprime[5];
+
+ memcpy(origx, x, 5 * sizeof(limb));
+ fsum(x, z);
+ fdifference_backwards(z, origx); // does x - z
+
+ memcpy(origxprime, xprime, sizeof(limb) * 5);
+ fsum(xprime, zprime);
+ fdifference_backwards(zprime, origxprime);
+ fmul(xxprime, xprime, z);
+ fmul(zzprime, x, zprime);
+ memcpy(origxprime, xxprime, sizeof(limb) * 5);
+ fsum(xxprime, zzprime);
+ fdifference_backwards(zzprime, origxprime);
+ fsquare_times(x3, xxprime, 1);
+ fsquare_times(zzzprime, zzprime, 1);
+ fmul(z3, zzzprime, qmqp);
+
+ fsquare_times(xx, x, 1);
+ fsquare_times(zz, z, 1);
+ fmul(x2, xx, zz);
+ fdifference_backwards(zz, xx); // does zz = xx - zz
+ fscalar_product(zzz, zz, 121665);
+ fsum(zzz, xx);
+ fmul(z2, zz, zzz);
+}
+
+// -----------------------------------------------------------------------------
+// Maybe swap the contents of two limb arrays (@a and @b), each @len elements
+// long. Perform the swap iff @swap is non-zero.
+//
+// This function performs the swap without leaking any side-channel
+// information.
+// -----------------------------------------------------------------------------
+static void
+swap_conditional(limb a[5], limb b[5], limb iswap) {
+ unsigned i;
+ const limb swap = -iswap;
+
+ for (i = 0; i < 5; ++i) {
+ const limb x = swap & (a[i] ^ b[i]);
+ a[i] ^= x;
+ b[i] ^= x;
+ }
+}
+
+/* Calculates nQ where Q is the x-coordinate of a point on the curve
+ *
+ * resultx/resultz: the x coordinate of the resulting curve point (short form)
+ * n: a little endian, 32-byte number
+ * q: a point of the curve (short form)
+ */
+static void
+cmult(limb *resultx, limb *resultz, const u8 *n, const limb *q) {
+ limb a[5] = {0}, b[5] = {1}, c[5] = {1}, d[5] = {0};
+ limb *nqpqx = a, *nqpqz = b, *nqx = c, *nqz = d, *t;
+ limb e[5] = {0}, f[5] = {1}, g[5] = {0}, h[5] = {1};
+ limb *nqpqx2 = e, *nqpqz2 = f, *nqx2 = g, *nqz2 = h;
+
+ unsigned i, j;
+
+ memcpy(nqpqx, q, sizeof(limb) * 5);
+
+ for (i = 0; i < 32; ++i) {
+ u8 byte = n[31 - i];
+ for (j = 0; j < 8; ++j) {
+ const limb bit = byte >> 7;
+
+ swap_conditional(nqx, nqpqx, bit);
+ swap_conditional(nqz, nqpqz, bit);
+ fmonty(nqx2, nqz2,
+ nqpqx2, nqpqz2,
+ nqx, nqz,
+ nqpqx, nqpqz,
+ q);
+ swap_conditional(nqx2, nqpqx2, bit);
+ swap_conditional(nqz2, nqpqz2, bit);
+
+ t = nqx;
+ nqx = nqx2;
+ nqx2 = t;
+ t = nqz;
+ nqz = nqz2;
+ nqz2 = t;
+ t = nqpqx;
+ nqpqx = nqpqx2;
+ nqpqx2 = t;
+ t = nqpqz;
+ nqpqz = nqpqz2;
+ nqpqz2 = t;
+
+ byte <<= 1;
+ }
+ }
+
+ memcpy(resultx, nqx, sizeof(limb) * 5);
+ memcpy(resultz, nqz, sizeof(limb) * 5);
+}
+
+
+// -----------------------------------------------------------------------------
+// Shamelessly copied from djb's code, tightened a little
+// -----------------------------------------------------------------------------
+static void
+crecip(felem out, const felem z) {
+ felem a,t0,b,c;
+
+ /* 2 */ fsquare_times(a, z, 1); // a = 2
+ /* 8 */ fsquare_times(t0, a, 2);
+ /* 9 */ fmul(b, t0, z); // b = 9
+ /* 11 */ fmul(a, b, a); // a = 11
+ /* 22 */ fsquare_times(t0, a, 1);
+ /* 2^5 - 2^0 = 31 */ fmul(b, t0, b);
+ /* 2^10 - 2^5 */ fsquare_times(t0, b, 5);
+ /* 2^10 - 2^0 */ fmul(b, t0, b);
+ /* 2^20 - 2^10 */ fsquare_times(t0, b, 10);
+ /* 2^20 - 2^0 */ fmul(c, t0, b);
+ /* 2^40 - 2^20 */ fsquare_times(t0, c, 20);
+ /* 2^40 - 2^0 */ fmul(t0, t0, c);
+ /* 2^50 - 2^10 */ fsquare_times(t0, t0, 10);
+ /* 2^50 - 2^0 */ fmul(b, t0, b);
+ /* 2^100 - 2^50 */ fsquare_times(t0, b, 50);
+ /* 2^100 - 2^0 */ fmul(c, t0, b);
+ /* 2^200 - 2^100 */ fsquare_times(t0, c, 100);
+ /* 2^200 - 2^0 */ fmul(t0, t0, c);
+ /* 2^250 - 2^50 */ fsquare_times(t0, t0, 50);
+ /* 2^250 - 2^0 */ fmul(t0, t0, b);
+ /* 2^255 - 2^5 */ fsquare_times(t0, t0, 5);
+ /* 2^255 - 21 */ fmul(out, t0, a);
+}
+
+int curve25519_donna(u8 *, const u8 *, const u8 *);
+
+int
+curve25519_donna(u8 *mypublic, const u8 *secret, const u8 *basepoint) {
+ limb bp[5], x[5], z[5], zmone[5];
+ uint8_t e[32];
+ int i;
+
+ for (i = 0;i < 32;++i) e[i] = secret[i];
+ e[0] &= 248;
+ e[31] &= 127;
+ e[31] |= 64;
+
+ fexpand(bp, basepoint);
+ cmult(x, z, e, bp);
+ crecip(zmone, z);
+ fmul(z, x, zmone);
+ fcontract(mypublic, z);
+ return 0;
+}
--- /dev/null
+/* Copyright 2008, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * curve25519-donna: Curve25519 elliptic curve, public key function
+ *
+ * http://code.google.com/p/curve25519-donna/
+ *
+ * Adam Langley <agl@imperialviolet.org>
+ *
+ * Derived from public domain C code by Daniel J. Bernstein <djb@cr.yp.to>
+ *
+ * More information about curve25519 can be found here
+ * http://cr.yp.to/ecdh.html
+ *
+ * djb's sample implementation of curve25519 is written in a special assembly
+ * language called qhasm and uses the floating point registers.
+ *
+ * This is, almost, a clean room reimplementation from the curve25519 paper. It
+ * uses many of the tricks described therein. Only the crecip function is taken
+ * from the sample implementation. */
+
+#include <string.h>
+#include <stdint.h>
+
+#ifdef _MSC_VER
+#define inline __inline
+#endif
+
+typedef uint8_t u8;
+typedef int32_t s32;
+typedef int64_t limb;
+
+/* Field element representation:
+ *
+ * Field elements are written as an array of signed, 64-bit limbs, least
+ * significant first. The value of the field element is:
+ * x[0] + 2^26·x[1] + x^51·x[2] + 2^102·x[3] + ...
+ *
+ * i.e. the limbs are 26, 25, 26, 25, ... bits wide. */
+
+/* Sum two numbers: output += in */
+static void fsum(limb *output, const limb *in) {
+ unsigned i;
+ for (i = 0; i < 10; i += 2) {
+ output[0+i] = output[0+i] + in[0+i];
+ output[1+i] = output[1+i] + in[1+i];
+ }
+}
+
+/* Find the difference of two numbers: output = in - output
+ * (note the order of the arguments!). */
+static void fdifference(limb *output, const limb *in) {
+ unsigned i;
+ for (i = 0; i < 10; ++i) {
+ output[i] = in[i] - output[i];
+ }
+}
+
+/* Multiply a number by a scalar: output = in * scalar */
+static void fscalar_product(limb *output, const limb *in, const limb scalar) {
+ unsigned i;
+ for (i = 0; i < 10; ++i) {
+ output[i] = in[i] * scalar;
+ }
+}
+
+/* Multiply two numbers: output = in2 * in
+ *
+ * output must be distinct to both inputs. The inputs are reduced coefficient
+ * form, the output is not.
+ *
+ * output[x] <= 14 * the largest product of the input limbs. */
+static void fproduct(limb *output, const limb *in2, const limb *in) {
+ output[0] = ((limb) ((s32) in2[0])) * ((s32) in[0]);
+ output[1] = ((limb) ((s32) in2[0])) * ((s32) in[1]) +
+ ((limb) ((s32) in2[1])) * ((s32) in[0]);
+ output[2] = 2 * ((limb) ((s32) in2[1])) * ((s32) in[1]) +
+ ((limb) ((s32) in2[0])) * ((s32) in[2]) +
+ ((limb) ((s32) in2[2])) * ((s32) in[0]);
+ output[3] = ((limb) ((s32) in2[1])) * ((s32) in[2]) +
+ ((limb) ((s32) in2[2])) * ((s32) in[1]) +
+ ((limb) ((s32) in2[0])) * ((s32) in[3]) +
+ ((limb) ((s32) in2[3])) * ((s32) in[0]);
+ output[4] = ((limb) ((s32) in2[2])) * ((s32) in[2]) +
+ 2 * (((limb) ((s32) in2[1])) * ((s32) in[3]) +
+ ((limb) ((s32) in2[3])) * ((s32) in[1])) +
+ ((limb) ((s32) in2[0])) * ((s32) in[4]) +
+ ((limb) ((s32) in2[4])) * ((s32) in[0]);
+ output[5] = ((limb) ((s32) in2[2])) * ((s32) in[3]) +
+ ((limb) ((s32) in2[3])) * ((s32) in[2]) +
+ ((limb) ((s32) in2[1])) * ((s32) in[4]) +
+ ((limb) ((s32) in2[4])) * ((s32) in[1]) +
+ ((limb) ((s32) in2[0])) * ((s32) in[5]) +
+ ((limb) ((s32) in2[5])) * ((s32) in[0]);
+ output[6] = 2 * (((limb) ((s32) in2[3])) * ((s32) in[3]) +
+ ((limb) ((s32) in2[1])) * ((s32) in[5]) +
+ ((limb) ((s32) in2[5])) * ((s32) in[1])) +
+ ((limb) ((s32) in2[2])) * ((s32) in[4]) +
+ ((limb) ((s32) in2[4])) * ((s32) in[2]) +
+ ((limb) ((s32) in2[0])) * ((s32) in[6]) +
+ ((limb) ((s32) in2[6])) * ((s32) in[0]);
+ output[7] = ((limb) ((s32) in2[3])) * ((s32) in[4]) +
+ ((limb) ((s32) in2[4])) * ((s32) in[3]) +
+ ((limb) ((s32) in2[2])) * ((s32) in[5]) +
+ ((limb) ((s32) in2[5])) * ((s32) in[2]) +
+ ((limb) ((s32) in2[1])) * ((s32) in[6]) +
+ ((limb) ((s32) in2[6])) * ((s32) in[1]) +
+ ((limb) ((s32) in2[0])) * ((s32) in[7]) +
+ ((limb) ((s32) in2[7])) * ((s32) in[0]);
+ output[8] = ((limb) ((s32) in2[4])) * ((s32) in[4]) +
+ 2 * (((limb) ((s32) in2[3])) * ((s32) in[5]) +
+ ((limb) ((s32) in2[5])) * ((s32) in[3]) +
+ ((limb) ((s32) in2[1])) * ((s32) in[7]) +
+ ((limb) ((s32) in2[7])) * ((s32) in[1])) +
+ ((limb) ((s32) in2[2])) * ((s32) in[6]) +
+ ((limb) ((s32) in2[6])) * ((s32) in[2]) +
+ ((limb) ((s32) in2[0])) * ((s32) in[8]) +
+ ((limb) ((s32) in2[8])) * ((s32) in[0]);
+ output[9] = ((limb) ((s32) in2[4])) * ((s32) in[5]) +
+ ((limb) ((s32) in2[5])) * ((s32) in[4]) +
+ ((limb) ((s32) in2[3])) * ((s32) in[6]) +
+ ((limb) ((s32) in2[6])) * ((s32) in[3]) +
+ ((limb) ((s32) in2[2])) * ((s32) in[7]) +
+ ((limb) ((s32) in2[7])) * ((s32) in[2]) +
+ ((limb) ((s32) in2[1])) * ((s32) in[8]) +
+ ((limb) ((s32) in2[8])) * ((s32) in[1]) +
+ ((limb) ((s32) in2[0])) * ((s32) in[9]) +
+ ((limb) ((s32) in2[9])) * ((s32) in[0]);
+ output[10] = 2 * (((limb) ((s32) in2[5])) * ((s32) in[5]) +
+ ((limb) ((s32) in2[3])) * ((s32) in[7]) +
+ ((limb) ((s32) in2[7])) * ((s32) in[3]) +
+ ((limb) ((s32) in2[1])) * ((s32) in[9]) +
+ ((limb) ((s32) in2[9])) * ((s32) in[1])) +
+ ((limb) ((s32) in2[4])) * ((s32) in[6]) +
+ ((limb) ((s32) in2[6])) * ((s32) in[4]) +
+ ((limb) ((s32) in2[2])) * ((s32) in[8]) +
+ ((limb) ((s32) in2[8])) * ((s32) in[2]);
+ output[11] = ((limb) ((s32) in2[5])) * ((s32) in[6]) +
+ ((limb) ((s32) in2[6])) * ((s32) in[5]) +
+ ((limb) ((s32) in2[4])) * ((s32) in[7]) +
+ ((limb) ((s32) in2[7])) * ((s32) in[4]) +
+ ((limb) ((s32) in2[3])) * ((s32) in[8]) +
+ ((limb) ((s32) in2[8])) * ((s32) in[3]) +
+ ((limb) ((s32) in2[2])) * ((s32) in[9]) +
+ ((limb) ((s32) in2[9])) * ((s32) in[2]);
+ output[12] = ((limb) ((s32) in2[6])) * ((s32) in[6]) +
+ 2 * (((limb) ((s32) in2[5])) * ((s32) in[7]) +
+ ((limb) ((s32) in2[7])) * ((s32) in[5]) +
+ ((limb) ((s32) in2[3])) * ((s32) in[9]) +
+ ((limb) ((s32) in2[9])) * ((s32) in[3])) +
+ ((limb) ((s32) in2[4])) * ((s32) in[8]) +
+ ((limb) ((s32) in2[8])) * ((s32) in[4]);
+ output[13] = ((limb) ((s32) in2[6])) * ((s32) in[7]) +
+ ((limb) ((s32) in2[7])) * ((s32) in[6]) +
+ ((limb) ((s32) in2[5])) * ((s32) in[8]) +
+ ((limb) ((s32) in2[8])) * ((s32) in[5]) +
+ ((limb) ((s32) in2[4])) * ((s32) in[9]) +
+ ((limb) ((s32) in2[9])) * ((s32) in[4]);
+ output[14] = 2 * (((limb) ((s32) in2[7])) * ((s32) in[7]) +
+ ((limb) ((s32) in2[5])) * ((s32) in[9]) +
+ ((limb) ((s32) in2[9])) * ((s32) in[5])) +
+ ((limb) ((s32) in2[6])) * ((s32) in[8]) +
+ ((limb) ((s32) in2[8])) * ((s32) in[6]);
+ output[15] = ((limb) ((s32) in2[7])) * ((s32) in[8]) +
+ ((limb) ((s32) in2[8])) * ((s32) in[7]) +
+ ((limb) ((s32) in2[6])) * ((s32) in[9]) +
+ ((limb) ((s32) in2[9])) * ((s32) in[6]);
+ output[16] = ((limb) ((s32) in2[8])) * ((s32) in[8]) +
+ 2 * (((limb) ((s32) in2[7])) * ((s32) in[9]) +
+ ((limb) ((s32) in2[9])) * ((s32) in[7]));
+ output[17] = ((limb) ((s32) in2[8])) * ((s32) in[9]) +
+ ((limb) ((s32) in2[9])) * ((s32) in[8]);
+ output[18] = 2 * ((limb) ((s32) in2[9])) * ((s32) in[9]);
+}
+
+/* Reduce a long form to a short form by taking the input mod 2^255 - 19.
+ *
+ * On entry: |output[i]| < 14*2^54
+ * On exit: |output[0..8]| < 280*2^54 */
+static void freduce_degree(limb *output) {
+ /* Each of these shifts and adds ends up multiplying the value by 19.
+ *
+ * For output[0..8], the absolute entry value is < 14*2^54 and we add, at
+ * most, 19*14*2^54 thus, on exit, |output[0..8]| < 280*2^54. */
+ output[8] += output[18] << 4;
+ output[8] += output[18] << 1;
+ output[8] += output[18];
+ output[7] += output[17] << 4;
+ output[7] += output[17] << 1;
+ output[7] += output[17];
+ output[6] += output[16] << 4;
+ output[6] += output[16] << 1;
+ output[6] += output[16];
+ output[5] += output[15] << 4;
+ output[5] += output[15] << 1;
+ output[5] += output[15];
+ output[4] += output[14] << 4;
+ output[4] += output[14] << 1;
+ output[4] += output[14];
+ output[3] += output[13] << 4;
+ output[3] += output[13] << 1;
+ output[3] += output[13];
+ output[2] += output[12] << 4;
+ output[2] += output[12] << 1;
+ output[2] += output[12];
+ output[1] += output[11] << 4;
+ output[1] += output[11] << 1;
+ output[1] += output[11];
+ output[0] += output[10] << 4;
+ output[0] += output[10] << 1;
+ output[0] += output[10];
+}
+
+#if (-1 & 3) != 3
+#error "This code only works on a two's complement system"
+#endif
+
+/* return v / 2^26, using only shifts and adds.
+ *
+ * On entry: v can take any value. */
+static inline limb
+div_by_2_26(const limb v)
+{
+ /* High word of v; no shift needed. */
+ const uint32_t highword = (uint32_t) (((uint64_t) v) >> 32);
+ /* Set to all 1s if v was negative; else set to 0s. */
+ const int32_t sign = ((int32_t) highword) >> 31;
+ /* Set to 0x3ffffff if v was negative; else set to 0. */
+ const int32_t roundoff = ((uint32_t) sign) >> 6;
+ /* Should return v / (1<<26) */
+ return (v + roundoff) >> 26;
+}
+
+/* return v / (2^25), using only shifts and adds.
+ *
+ * On entry: v can take any value. */
+static inline limb
+div_by_2_25(const limb v)
+{
+ /* High word of v; no shift needed*/
+ const uint32_t highword = (uint32_t) (((uint64_t) v) >> 32);
+ /* Set to all 1s if v was negative; else set to 0s. */
+ const int32_t sign = ((int32_t) highword) >> 31;
+ /* Set to 0x1ffffff if v was negative; else set to 0. */
+ const int32_t roundoff = ((uint32_t) sign) >> 7;
+ /* Should return v / (1<<25) */
+ return (v + roundoff) >> 25;
+}
+
+/* Reduce all coefficients of the short form input so that |x| < 2^26.
+ *
+ * On entry: |output[i]| < 280*2^54 */
+static void freduce_coefficients(limb *output) {
+ unsigned i;
+
+ output[10] = 0;
+
+ for (i = 0; i < 10; i += 2) {
+ limb over = div_by_2_26(output[i]);
+ /* The entry condition (that |output[i]| < 280*2^54) means that over is, at
+ * most, 280*2^28 in the first iteration of this loop. This is added to the
+ * next limb and we can approximate the resulting bound of that limb by
+ * 281*2^54. */
+ output[i] -= over << 26;
+ output[i+1] += over;
+
+ /* For the first iteration, |output[i+1]| < 281*2^54, thus |over| <
+ * 281*2^29. When this is added to the next limb, the resulting bound can
+ * be approximated as 281*2^54.
+ *
+ * For subsequent iterations of the loop, 281*2^54 remains a conservative
+ * bound and no overflow occurs. */
+ over = div_by_2_25(output[i+1]);
+ output[i+1] -= over << 25;
+ output[i+2] += over;
+ }
+ /* Now |output[10]| < 281*2^29 and all other coefficients are reduced. */
+ output[0] += output[10] << 4;
+ output[0] += output[10] << 1;
+ output[0] += output[10];
+
+ output[10] = 0;
+
+ /* Now output[1..9] are reduced, and |output[0]| < 2^26 + 19*281*2^29
+ * So |over| will be no more than 2^16. */
+ {
+ limb over = div_by_2_26(output[0]);
+ output[0] -= over << 26;
+ output[1] += over;
+ }
+
+ /* Now output[0,2..9] are reduced, and |output[1]| < 2^25 + 2^16 < 2^26. The
+ * bound on |output[1]| is sufficient to meet our needs. */
+}
+
+/* A helpful wrapper around fproduct: output = in * in2.
+ *
+ * On entry: |in[i]| < 2^27 and |in2[i]| < 2^27.
+ *
+ * output must be distinct to both inputs. The output is reduced degree
+ * (indeed, one need only provide storage for 10 limbs) and |output[i]| < 2^26. */
+static void
+fmul(limb *output, const limb *in, const limb *in2) {
+ limb t[19];
+ fproduct(t, in, in2);
+ /* |t[i]| < 14*2^54 */
+ freduce_degree(t);
+ freduce_coefficients(t);
+ /* |t[i]| < 2^26 */
+ memcpy(output, t, sizeof(limb) * 10);
+}
+
+/* Square a number: output = in**2
+ *
+ * output must be distinct from the input. The inputs are reduced coefficient
+ * form, the output is not.
+ *
+ * output[x] <= 14 * the largest product of the input limbs. */
+static void fsquare_inner(limb *output, const limb *in) {
+ output[0] = ((limb) ((s32) in[0])) * ((s32) in[0]);
+ output[1] = 2 * ((limb) ((s32) in[0])) * ((s32) in[1]);
+ output[2] = 2 * (((limb) ((s32) in[1])) * ((s32) in[1]) +
+ ((limb) ((s32) in[0])) * ((s32) in[2]));
+ output[3] = 2 * (((limb) ((s32) in[1])) * ((s32) in[2]) +
+ ((limb) ((s32) in[0])) * ((s32) in[3]));
+ output[4] = ((limb) ((s32) in[2])) * ((s32) in[2]) +
+ 4 * ((limb) ((s32) in[1])) * ((s32) in[3]) +
+ 2 * ((limb) ((s32) in[0])) * ((s32) in[4]);
+ output[5] = 2 * (((limb) ((s32) in[2])) * ((s32) in[3]) +
+ ((limb) ((s32) in[1])) * ((s32) in[4]) +
+ ((limb) ((s32) in[0])) * ((s32) in[5]));
+ output[6] = 2 * (((limb) ((s32) in[3])) * ((s32) in[3]) +
+ ((limb) ((s32) in[2])) * ((s32) in[4]) +
+ ((limb) ((s32) in[0])) * ((s32) in[6]) +
+ 2 * ((limb) ((s32) in[1])) * ((s32) in[5]));
+ output[7] = 2 * (((limb) ((s32) in[3])) * ((s32) in[4]) +
+ ((limb) ((s32) in[2])) * ((s32) in[5]) +
+ ((limb) ((s32) in[1])) * ((s32) in[6]) +
+ ((limb) ((s32) in[0])) * ((s32) in[7]));
+ output[8] = ((limb) ((s32) in[4])) * ((s32) in[4]) +
+ 2 * (((limb) ((s32) in[2])) * ((s32) in[6]) +
+ ((limb) ((s32) in[0])) * ((s32) in[8]) +
+ 2 * (((limb) ((s32) in[1])) * ((s32) in[7]) +
+ ((limb) ((s32) in[3])) * ((s32) in[5])));
+ output[9] = 2 * (((limb) ((s32) in[4])) * ((s32) in[5]) +
+ ((limb) ((s32) in[3])) * ((s32) in[6]) +
+ ((limb) ((s32) in[2])) * ((s32) in[7]) +
+ ((limb) ((s32) in[1])) * ((s32) in[8]) +
+ ((limb) ((s32) in[0])) * ((s32) in[9]));
+ output[10] = 2 * (((limb) ((s32) in[5])) * ((s32) in[5]) +
+ ((limb) ((s32) in[4])) * ((s32) in[6]) +
+ ((limb) ((s32) in[2])) * ((s32) in[8]) +
+ 2 * (((limb) ((s32) in[3])) * ((s32) in[7]) +
+ ((limb) ((s32) in[1])) * ((s32) in[9])));
+ output[11] = 2 * (((limb) ((s32) in[5])) * ((s32) in[6]) +
+ ((limb) ((s32) in[4])) * ((s32) in[7]) +
+ ((limb) ((s32) in[3])) * ((s32) in[8]) +
+ ((limb) ((s32) in[2])) * ((s32) in[9]));
+ output[12] = ((limb) ((s32) in[6])) * ((s32) in[6]) +
+ 2 * (((limb) ((s32) in[4])) * ((s32) in[8]) +
+ 2 * (((limb) ((s32) in[5])) * ((s32) in[7]) +
+ ((limb) ((s32) in[3])) * ((s32) in[9])));
+ output[13] = 2 * (((limb) ((s32) in[6])) * ((s32) in[7]) +
+ ((limb) ((s32) in[5])) * ((s32) in[8]) +
+ ((limb) ((s32) in[4])) * ((s32) in[9]));
+ output[14] = 2 * (((limb) ((s32) in[7])) * ((s32) in[7]) +
+ ((limb) ((s32) in[6])) * ((s32) in[8]) +
+ 2 * ((limb) ((s32) in[5])) * ((s32) in[9]));
+ output[15] = 2 * (((limb) ((s32) in[7])) * ((s32) in[8]) +
+ ((limb) ((s32) in[6])) * ((s32) in[9]));
+ output[16] = ((limb) ((s32) in[8])) * ((s32) in[8]) +
+ 4 * ((limb) ((s32) in[7])) * ((s32) in[9]);
+ output[17] = 2 * ((limb) ((s32) in[8])) * ((s32) in[9]);
+ output[18] = 2 * ((limb) ((s32) in[9])) * ((s32) in[9]);
+}
+
+/* fsquare sets output = in^2.
+ *
+ * On entry: The |in| argument is in reduced coefficients form and |in[i]| <
+ * 2^27.
+ *
+ * On exit: The |output| argument is in reduced coefficients form (indeed, one
+ * need only provide storage for 10 limbs) and |out[i]| < 2^26. */
+static void
+fsquare(limb *output, const limb *in) {
+ limb t[19];
+ fsquare_inner(t, in);
+ /* |t[i]| < 14*2^54 because the largest product of two limbs will be <
+ * 2^(27+27) and fsquare_inner adds together, at most, 14 of those
+ * products. */
+ freduce_degree(t);
+ freduce_coefficients(t);
+ /* |t[i]| < 2^26 */
+ memcpy(output, t, sizeof(limb) * 10);
+}
+
+/* Take a little-endian, 32-byte number and expand it into polynomial form */
+static void
+fexpand(limb *output, const u8 *input) {
+#define F(n,start,shift,mask) \
+ output[n] = ((((limb) input[start + 0]) | \
+ ((limb) input[start + 1]) << 8 | \
+ ((limb) input[start + 2]) << 16 | \
+ ((limb) input[start + 3]) << 24) >> shift) & mask;
+ F(0, 0, 0, 0x3ffffff);
+ F(1, 3, 2, 0x1ffffff);
+ F(2, 6, 3, 0x3ffffff);
+ F(3, 9, 5, 0x1ffffff);
+ F(4, 12, 6, 0x3ffffff);
+ F(5, 16, 0, 0x1ffffff);
+ F(6, 19, 1, 0x3ffffff);
+ F(7, 22, 3, 0x1ffffff);
+ F(8, 25, 4, 0x3ffffff);
+ F(9, 28, 6, 0x1ffffff);
+#undef F
+}
+
+#if (-32 >> 1) != -16
+#error "This code only works when >> does sign-extension on negative numbers"
+#endif
+
+/* s32_eq returns 0xffffffff iff a == b and zero otherwise. */
+static s32 s32_eq(s32 a, s32 b) {
+ a = ~(a ^ b);
+ a &= a << 16;
+ a &= a << 8;
+ a &= a << 4;
+ a &= a << 2;
+ a &= a << 1;
+ return a >> 31;
+}
+
+/* s32_gte returns 0xffffffff if a >= b and zero otherwise, where a and b are
+ * both non-negative. */
+static s32 s32_gte(s32 a, s32 b) {
+ a -= b;
+ /* a >= 0 iff a >= b. */
+ return ~(a >> 31);
+}
+
+/* Take a fully reduced polynomial form number and contract it into a
+ * little-endian, 32-byte array.
+ *
+ * On entry: |input_limbs[i]| < 2^26 */
+static void
+fcontract(u8 *output, limb *input_limbs) {
+ int i;
+ int j;
+ s32 input[10];
+ s32 mask;
+
+ /* |input_limbs[i]| < 2^26, so it's valid to convert to an s32. */
+ for (i = 0; i < 10; i++) {
+ input[i] = input_limbs[i];
+ }
+
+ for (j = 0; j < 2; ++j) {
+ for (i = 0; i < 9; ++i) {
+ if ((i & 1) == 1) {
+ /* This calculation is a time-invariant way to make input[i]
+ * non-negative by borrowing from the next-larger limb. */
+ const s32 mask = input[i] >> 31;
+ const s32 carry = -((input[i] & mask) >> 25);
+ input[i] = input[i] + (carry << 25);
+ input[i+1] = input[i+1] - carry;
+ } else {
+ const s32 mask = input[i] >> 31;
+ const s32 carry = -((input[i] & mask) >> 26);
+ input[i] = input[i] + (carry << 26);
+ input[i+1] = input[i+1] - carry;
+ }
+ }
+
+ /* There's no greater limb for input[9] to borrow from, but we can multiply
+ * by 19 and borrow from input[0], which is valid mod 2^255-19. */
+ {
+ const s32 mask = input[9] >> 31;
+ const s32 carry = -((input[9] & mask) >> 25);
+ input[9] = input[9] + (carry << 25);
+ input[0] = input[0] - (carry * 19);
+ }
+
+ /* After the first iteration, input[1..9] are non-negative and fit within
+ * 25 or 26 bits, depending on position. However, input[0] may be
+ * negative. */
+ }
+
+ /* The first borrow-propagation pass above ended with every limb
+ except (possibly) input[0] non-negative.
+
+ If input[0] was negative after the first pass, then it was because of a
+ carry from input[9]. On entry, input[9] < 2^26 so the carry was, at most,
+ one, since (2**26-1) >> 25 = 1. Thus input[0] >= -19.
+
+ In the second pass, each limb is decreased by at most one. Thus the second
+ borrow-propagation pass could only have wrapped around to decrease
+ input[0] again if the first pass left input[0] negative *and* input[1]
+ through input[9] were all zero. In that case, input[1] is now 2^25 - 1,
+ and this last borrow-propagation step will leave input[1] non-negative. */
+ {
+ const s32 mask = input[0] >> 31;
+ const s32 carry = -((input[0] & mask) >> 26);
+ input[0] = input[0] + (carry << 26);
+ input[1] = input[1] - carry;
+ }
+
+ /* All input[i] are now non-negative. However, there might be values between
+ * 2^25 and 2^26 in a limb which is, nominally, 25 bits wide. */
+ for (j = 0; j < 2; j++) {
+ for (i = 0; i < 9; i++) {
+ if ((i & 1) == 1) {
+ const s32 carry = input[i] >> 25;
+ input[i] &= 0x1ffffff;
+ input[i+1] += carry;
+ } else {
+ const s32 carry = input[i] >> 26;
+ input[i] &= 0x3ffffff;
+ input[i+1] += carry;
+ }
+ }
+
+ {
+ const s32 carry = input[9] >> 25;
+ input[9] &= 0x1ffffff;
+ input[0] += 19*carry;
+ }
+ }
+
+ /* If the first carry-chain pass, just above, ended up with a carry from
+ * input[9], and that caused input[0] to be out-of-bounds, then input[0] was
+ * < 2^26 + 2*19, because the carry was, at most, two.
+ *
+ * If the second pass carried from input[9] again then input[0] is < 2*19 and
+ * the input[9] -> input[0] carry didn't push input[0] out of bounds. */
+
+ /* It still remains the case that input might be between 2^255-19 and 2^255.
+ * In this case, input[1..9] must take their maximum value and input[0] must
+ * be >= (2^255-19) & 0x3ffffff, which is 0x3ffffed. */
+ mask = s32_gte(input[0], 0x3ffffed);
+ for (i = 1; i < 10; i++) {
+ if ((i & 1) == 1) {
+ mask &= s32_eq(input[i], 0x1ffffff);
+ } else {
+ mask &= s32_eq(input[i], 0x3ffffff);
+ }
+ }
+
+ /* mask is either 0xffffffff (if input >= 2^255-19) and zero otherwise. Thus
+ * this conditionally subtracts 2^255-19. */
+ input[0] -= mask & 0x3ffffed;
+
+ for (i = 1; i < 10; i++) {
+ if ((i & 1) == 1) {
+ input[i] -= mask & 0x1ffffff;
+ } else {
+ input[i] -= mask & 0x3ffffff;
+ }
+ }
+
+ input[1] <<= 2;
+ input[2] <<= 3;
+ input[3] <<= 5;
+ input[4] <<= 6;
+ input[6] <<= 1;
+ input[7] <<= 3;
+ input[8] <<= 4;
+ input[9] <<= 6;
+#define F(i, s) \
+ output[s+0] |= input[i] & 0xff; \
+ output[s+1] = (input[i] >> 8) & 0xff; \
+ output[s+2] = (input[i] >> 16) & 0xff; \
+ output[s+3] = (input[i] >> 24) & 0xff;
+ output[0] = 0;
+ output[16] = 0;
+ F(0,0);
+ F(1,3);
+ F(2,6);
+ F(3,9);
+ F(4,12);
+ F(5,16);
+ F(6,19);
+ F(7,22);
+ F(8,25);
+ F(9,28);
+#undef F
+}
+
+/* Input: Q, Q', Q-Q'
+ * Output: 2Q, Q+Q'
+ *
+ * x2 z3: long form
+ * x3 z3: long form
+ * x z: short form, destroyed
+ * xprime zprime: short form, destroyed
+ * qmqp: short form, preserved
+ *
+ * On entry and exit, the absolute value of the limbs of all inputs and outputs
+ * are < 2^26. */
+static void fmonty(limb *x2, limb *z2, /* output 2Q */
+ limb *x3, limb *z3, /* output Q + Q' */
+ limb *x, limb *z, /* input Q */
+ limb *xprime, limb *zprime, /* input Q' */
+ const limb *qmqp /* input Q - Q' */) {
+ limb origx[10], origxprime[10], zzz[19], xx[19], zz[19], xxprime[19],
+ zzprime[19], zzzprime[19], xxxprime[19];
+
+ memcpy(origx, x, 10 * sizeof(limb));
+ fsum(x, z);
+ /* |x[i]| < 2^27 */
+ fdifference(z, origx); /* does x - z */
+ /* |z[i]| < 2^27 */
+
+ memcpy(origxprime, xprime, sizeof(limb) * 10);
+ fsum(xprime, zprime);
+ /* |xprime[i]| < 2^27 */
+ fdifference(zprime, origxprime);
+ /* |zprime[i]| < 2^27 */
+ fproduct(xxprime, xprime, z);
+ /* |xxprime[i]| < 14*2^54: the largest product of two limbs will be <
+ * 2^(27+27) and fproduct adds together, at most, 14 of those products.
+ * (Approximating that to 2^58 doesn't work out.) */
+ fproduct(zzprime, x, zprime);
+ /* |zzprime[i]| < 14*2^54 */
+ freduce_degree(xxprime);
+ freduce_coefficients(xxprime);
+ /* |xxprime[i]| < 2^26 */
+ freduce_degree(zzprime);
+ freduce_coefficients(zzprime);
+ /* |zzprime[i]| < 2^26 */
+ memcpy(origxprime, xxprime, sizeof(limb) * 10);
+ fsum(xxprime, zzprime);
+ /* |xxprime[i]| < 2^27 */
+ fdifference(zzprime, origxprime);
+ /* |zzprime[i]| < 2^27 */
+ fsquare(xxxprime, xxprime);
+ /* |xxxprime[i]| < 2^26 */
+ fsquare(zzzprime, zzprime);
+ /* |zzzprime[i]| < 2^26 */
+ fproduct(zzprime, zzzprime, qmqp);
+ /* |zzprime[i]| < 14*2^52 */
+ freduce_degree(zzprime);
+ freduce_coefficients(zzprime);
+ /* |zzprime[i]| < 2^26 */
+ memcpy(x3, xxxprime, sizeof(limb) * 10);
+ memcpy(z3, zzprime, sizeof(limb) * 10);
+
+ fsquare(xx, x);
+ /* |xx[i]| < 2^26 */
+ fsquare(zz, z);
+ /* |zz[i]| < 2^26 */
+ fproduct(x2, xx, zz);
+ /* |x2[i]| < 14*2^52 */
+ freduce_degree(x2);
+ freduce_coefficients(x2);
+ /* |x2[i]| < 2^26 */
+ fdifference(zz, xx); // does zz = xx - zz
+ /* |zz[i]| < 2^27 */
+ memset(zzz + 10, 0, sizeof(limb) * 9);
+ fscalar_product(zzz, zz, 121665);
+ /* |zzz[i]| < 2^(27+17) */
+ /* No need to call freduce_degree here:
+ fscalar_product doesn't increase the degree of its input. */
+ freduce_coefficients(zzz);
+ /* |zzz[i]| < 2^26 */
+ fsum(zzz, xx);
+ /* |zzz[i]| < 2^27 */
+ fproduct(z2, zz, zzz);
+ /* |z2[i]| < 14*2^(26+27) */
+ freduce_degree(z2);
+ freduce_coefficients(z2);
+ /* |z2|i| < 2^26 */
+}
+
+/* Conditionally swap two reduced-form limb arrays if 'iswap' is 1, but leave
+ * them unchanged if 'iswap' is 0. Runs in data-invariant time to avoid
+ * side-channel attacks.
+ *
+ * NOTE that this function requires that 'iswap' be 1 or 0; other values give
+ * wrong results. Also, the two limb arrays must be in reduced-coefficient,
+ * reduced-degree form: the values in a[10..19] or b[10..19] aren't swapped,
+ * and all all values in a[0..9],b[0..9] must have magnitude less than
+ * INT32_MAX. */
+static void
+swap_conditional(limb a[19], limb b[19], limb iswap) {
+ unsigned i;
+ const s32 swap = (s32) -iswap;
+
+ for (i = 0; i < 10; ++i) {
+ const s32 x = swap & ( ((s32)a[i]) ^ ((s32)b[i]) );
+ a[i] = ((s32)a[i]) ^ x;
+ b[i] = ((s32)b[i]) ^ x;
+ }
+}
+
+/* Calculates nQ where Q is the x-coordinate of a point on the curve
+ *
+ * resultx/resultz: the x coordinate of the resulting curve point (short form)
+ * n: a little endian, 32-byte number
+ * q: a point of the curve (short form) */
+static void
+cmult(limb *resultx, limb *resultz, const u8 *n, const limb *q) {
+ limb a[19] = {0}, b[19] = {1}, c[19] = {1}, d[19] = {0};
+ limb *nqpqx = a, *nqpqz = b, *nqx = c, *nqz = d, *t;
+ limb e[19] = {0}, f[19] = {1}, g[19] = {0}, h[19] = {1};
+ limb *nqpqx2 = e, *nqpqz2 = f, *nqx2 = g, *nqz2 = h;
+
+ unsigned i, j;
+
+ memcpy(nqpqx, q, sizeof(limb) * 10);
+
+ for (i = 0; i < 32; ++i) {
+ u8 byte = n[31 - i];
+ for (j = 0; j < 8; ++j) {
+ const limb bit = byte >> 7;
+
+ swap_conditional(nqx, nqpqx, bit);
+ swap_conditional(nqz, nqpqz, bit);
+ fmonty(nqx2, nqz2,
+ nqpqx2, nqpqz2,
+ nqx, nqz,
+ nqpqx, nqpqz,
+ q);
+ swap_conditional(nqx2, nqpqx2, bit);
+ swap_conditional(nqz2, nqpqz2, bit);
+
+ t = nqx;
+ nqx = nqx2;
+ nqx2 = t;
+ t = nqz;
+ nqz = nqz2;
+ nqz2 = t;
+ t = nqpqx;
+ nqpqx = nqpqx2;
+ nqpqx2 = t;
+ t = nqpqz;
+ nqpqz = nqpqz2;
+ nqpqz2 = t;
+
+ byte <<= 1;
+ }
+ }
+
+ memcpy(resultx, nqx, sizeof(limb) * 10);
+ memcpy(resultz, nqz, sizeof(limb) * 10);
+}
+
+// -----------------------------------------------------------------------------
+// Shamelessly copied from djb's code
+// -----------------------------------------------------------------------------
+static void
+crecip(limb *out, const limb *z) {
+ limb z2[10];
+ limb z9[10];
+ limb z11[10];
+ limb z2_5_0[10];
+ limb z2_10_0[10];
+ limb z2_20_0[10];
+ limb z2_50_0[10];
+ limb z2_100_0[10];
+ limb t0[10];
+ limb t1[10];
+ int i;
+
+ /* 2 */ fsquare(z2,z);
+ /* 4 */ fsquare(t1,z2);
+ /* 8 */ fsquare(t0,t1);
+ /* 9 */ fmul(z9,t0,z);
+ /* 11 */ fmul(z11,z9,z2);
+ /* 22 */ fsquare(t0,z11);
+ /* 2^5 - 2^0 = 31 */ fmul(z2_5_0,t0,z9);
+
+ /* 2^6 - 2^1 */ fsquare(t0,z2_5_0);
+ /* 2^7 - 2^2 */ fsquare(t1,t0);
+ /* 2^8 - 2^3 */ fsquare(t0,t1);
+ /* 2^9 - 2^4 */ fsquare(t1,t0);
+ /* 2^10 - 2^5 */ fsquare(t0,t1);
+ /* 2^10 - 2^0 */ fmul(z2_10_0,t0,z2_5_0);
+
+ /* 2^11 - 2^1 */ fsquare(t0,z2_10_0);
+ /* 2^12 - 2^2 */ fsquare(t1,t0);
+ /* 2^20 - 2^10 */ for (i = 2;i < 10;i += 2) { fsquare(t0,t1); fsquare(t1,t0); }
+ /* 2^20 - 2^0 */ fmul(z2_20_0,t1,z2_10_0);
+
+ /* 2^21 - 2^1 */ fsquare(t0,z2_20_0);
+ /* 2^22 - 2^2 */ fsquare(t1,t0);
+ /* 2^40 - 2^20 */ for (i = 2;i < 20;i += 2) { fsquare(t0,t1); fsquare(t1,t0); }
+ /* 2^40 - 2^0 */ fmul(t0,t1,z2_20_0);
+
+ /* 2^41 - 2^1 */ fsquare(t1,t0);
+ /* 2^42 - 2^2 */ fsquare(t0,t1);
+ /* 2^50 - 2^10 */ for (i = 2;i < 10;i += 2) { fsquare(t1,t0); fsquare(t0,t1); }
+ /* 2^50 - 2^0 */ fmul(z2_50_0,t0,z2_10_0);
+
+ /* 2^51 - 2^1 */ fsquare(t0,z2_50_0);
+ /* 2^52 - 2^2 */ fsquare(t1,t0);
+ /* 2^100 - 2^50 */ for (i = 2;i < 50;i += 2) { fsquare(t0,t1); fsquare(t1,t0); }
+ /* 2^100 - 2^0 */ fmul(z2_100_0,t1,z2_50_0);
+
+ /* 2^101 - 2^1 */ fsquare(t1,z2_100_0);
+ /* 2^102 - 2^2 */ fsquare(t0,t1);
+ /* 2^200 - 2^100 */ for (i = 2;i < 100;i += 2) { fsquare(t1,t0); fsquare(t0,t1); }
+ /* 2^200 - 2^0 */ fmul(t1,t0,z2_100_0);
+
+ /* 2^201 - 2^1 */ fsquare(t0,t1);
+ /* 2^202 - 2^2 */ fsquare(t1,t0);
+ /* 2^250 - 2^50 */ for (i = 2;i < 50;i += 2) { fsquare(t0,t1); fsquare(t1,t0); }
+ /* 2^250 - 2^0 */ fmul(t0,t1,z2_50_0);
+
+ /* 2^251 - 2^1 */ fsquare(t1,t0);
+ /* 2^252 - 2^2 */ fsquare(t0,t1);
+ /* 2^253 - 2^3 */ fsquare(t1,t0);
+ /* 2^254 - 2^4 */ fsquare(t0,t1);
+ /* 2^255 - 2^5 */ fsquare(t1,t0);
+ /* 2^255 - 21 */ fmul(out,t1,z11);
+}
+
+int
+curve25519_donna(u8 *mypublic, const u8 *secret, const u8 *basepoint) {
+ limb bp[10], x[10], z[11], zmone[10];
+ uint8_t e[32];
+ int i;
+
+ for (i = 0; i < 32; ++i) e[i] = secret[i];
+ e[0] &= 248;
+ e[31] &= 127;
+ e[31] |= 64;
+
+ fexpand(bp, basepoint);
+ cmult(x, z, e, bp);
+ crecip(zmone, z);
+ fmul(z, x, zmone);
+ fcontract(mypublic, z);
+ return 0;
+}
--- /dev/null
+Pod::Spec.new do |s|
+ s.name = "curve25519-donna"
+ s.version = "1.2.1"
+ s.summary = "Implementations of a fast elliptic-curve, Diffie-Hellman primitive"
+ s.description = <<-DESC
+ Curve25519 is a state-of-the-art Diffie-Hellman function suitable for a wide variety of applications.
+ DESC
+ s.homepage = "http://code.google.com/p/curve25519-donna"
+ s.license = 'BSD 3-Clause'
+ s.author = 'Dan Bernstein'
+ s.source = { :git => "https://github.com/agl/curve25519-donna.git", :tag => "1.2.1" }
+ s.source_files = 'curve25519-donna.c'
+end
--- /dev/null
+#! /usr/bin/env python
+
+from subprocess import Popen, PIPE
+from distutils.core import setup, Extension
+
+version = Popen(["git", "describe", "--tags"], stdout=PIPE).communicate()[0]\
+ .strip().decode("utf8")
+
+ext_modules = [Extension("curve25519._curve25519",
+ ["python-src/curve25519/curve25519module.c",
+ "curve25519-donna.c"],
+ )]
+
+short_description="Python wrapper for the Curve25519 cryptographic library"
+long_description="""\
+Curve25519 is a fast elliptic-curve key-agreement protocol, in which two
+parties Alice and Bob each generate a (public,private) keypair, exchange
+public keys, and can then compute the same shared key. Specifically, Alice
+computes F(Aprivate, Bpublic), Bob computes F(Bprivate, Apublic), and both
+get the same value (and nobody else can guess that shared value, even if they
+know Apublic and Bpublic).
+
+This is a Python wrapper for the portable 'curve25519-donna' implementation
+of this algorithm, written by Adam Langley, hosted at
+http://code.google.com/p/curve25519-donna/
+"""
+
+setup(name="curve25519-donna",
+ version=version,
+ description=short_description,
+ long_description=long_description,
+ author="Brian Warner",
+ author_email="warner-pycurve25519-donna@lothar.com",
+ license="BSD",
+ packages=["curve25519", "curve25519.test"],
+ package_dir={"curve25519": "python-src/curve25519"},
+ ext_modules=ext_modules,
+ )
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+#include <stdint.h>
+
+typedef uint8_t u8;
+
+extern void curve25519_donna(u8 *output, const u8 *secret, const u8 *bp);
+
+static uint64_t
+time_now() {
+ struct timeval tv;
+ uint64_t ret;
+
+ gettimeofday(&tv, NULL);
+ ret = tv.tv_sec;
+ ret *= 1000000;
+ ret += tv.tv_usec;
+
+ return ret;
+}
+
+int
+main() {
+ static const unsigned char basepoint[32] = {9};
+ unsigned char mysecret[32], mypublic[32];
+ unsigned i;
+ uint64_t start, end;
+
+ memset(mysecret, 42, 32);
+ mysecret[0] &= 248;
+ mysecret[31] &= 127;
+ mysecret[31] |= 64;
+
+ // Load the caches
+ for (i = 0; i < 1000; ++i) {
+ curve25519_donna(mypublic, mysecret, basepoint);
+ }
+
+ start = time_now();
+ for (i = 0; i < 30000; ++i) {
+ curve25519_donna(mypublic, mysecret, basepoint);
+ }
+ end = time_now();
+
+ printf("%luus\n", (unsigned long) ((end - start) / 30000));
+
+ return 0;
+}
--- /dev/null
+/*
+test-curve25519 version 20050915
+D. J. Bernstein
+Public domain.
+
+Tiny modifications by agl
+*/
+
+#include <stdio.h>
+
+extern void curve25519_donna(unsigned char *output, const unsigned char *a,
+ const unsigned char *b);
+void doit(unsigned char *ek,unsigned char *e,unsigned char *k);
+
+void doit(unsigned char *ek,unsigned char *e,unsigned char *k)
+{
+ int i;
+
+ for (i = 0;i < 32;++i) printf("%02x",(unsigned int) e[i]); printf(" ");
+ for (i = 0;i < 32;++i) printf("%02x",(unsigned int) k[i]); printf(" ");
+ curve25519_donna(ek,e,k);
+ for (i = 0;i < 32;++i) printf("%02x",(unsigned int) ek[i]); printf("\n");
+}
+
+unsigned char e1k[32];
+unsigned char e2k[32];
+unsigned char e1e2k[32];
+unsigned char e2e1k[32];
+unsigned char e1[32] = {3};
+unsigned char e2[32] = {5};
+unsigned char k[32] = {9};
+
+int
+main()
+{
+ int loop;
+ int i;
+
+ for (loop = 0;loop < 10000;++loop) {
+ doit(e1k,e1,k);
+ doit(e2e1k,e2,e1k);
+ doit(e2k,e2,k);
+ doit(e1e2k,e1,e2k);
+ for (i = 0;i < 32;++i) if (e1e2k[i] != e2e1k[i]) {
+ printf("fail\n");
+ return 1;
+ }
+ for (i = 0;i < 32;++i) e1[i] ^= e2k[i];
+ for (i = 0;i < 32;++i) e2[i] ^= e1k[i];
+ for (i = 0;i < 32;++i) k[i] ^= e1e2k[i];
+ }
+
+ return 0;
+}
--- /dev/null
+/* This file can be used to test whether the code handles non-canonical curve
+ * points (i.e. points with the 256th bit set) in the same way as the reference
+ * implementation. */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+extern void curve25519_donna(unsigned char *output, const unsigned char *a,
+ const unsigned char *b);
+int
+main()
+{
+ static const uint8_t point1[32] = {
+ 0x25,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ };
+ static const uint8_t point2[32] = {
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ };
+ static const uint8_t scalar[32] = { 1 };
+ uint8_t out1[32], out2[32];
+
+ curve25519_donna(out1, scalar, point1);
+ curve25519_donna(out2, scalar, point2);
+
+ if (0 == memcmp(out1, out2, sizeof(out1))) {
+ fprintf(stderr, "Top bit not ignored.\n");
+ return 1;
+ }
+
+ fprintf(stderr, "Top bit correctly ignored.\n");
+ return 0;
+}
--- /dev/null
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <math.h>
+
+extern void curve25519_donna(uint8_t *, const uint8_t *, const uint8_t *);
+extern uint64_t tsc_read();
+
+int
+main(int argc, char **argv) {
+ uint8_t private_key[32], public[32], peer1[32], peer2[32], output[32];
+ static const uint8_t basepoint[32] = {9};
+ unsigned i;
+ uint64_t sum = 0, sum_squares = 0, skipped = 0, mean;
+ static const unsigned count = 200000;
+
+ memset(private_key, 42, sizeof(private_key));
+
+ private_key[0] &= 248;
+ private_key[31] &= 127;
+ private_key[31] |= 64;
+
+ curve25519_donna(public, private_key, basepoint);
+ memset(peer1, 0, sizeof(peer1));
+ memset(peer2, 255, sizeof(peer2));
+
+ for (i = 0; i < count; ++i) {
+ const uint64_t start = tsc_read();
+ curve25519_donna(output, peer1, public);
+ const uint64_t end = tsc_read();
+ const uint64_t delta = end - start;
+ if (delta > 650000) {
+ // something terrible happened (task switch etc)
+ skipped++;
+ continue;
+ }
+ sum += delta;
+ sum_squares += (delta * delta);
+ }
+
+ mean = sum / ((uint64_t) count);
+ printf("all 0: mean:%lu sd:%f skipped:%lu\n",
+ mean,
+ sqrt((double)(sum_squares/((uint64_t) count) - mean*mean)),
+ skipped);
+
+ sum = sum_squares = skipped = 0;
+
+ for (i = 0; i < count; ++i) {
+ const uint64_t start = tsc_read();
+ curve25519_donna(output, peer2, public);
+ const uint64_t end = tsc_read();
+ const uint64_t delta = end - start;
+ if (delta > 650000) {
+ // something terrible happened (task switch etc)
+ skipped++;
+ continue;
+ }
+ sum += delta;
+ sum_squares += (delta * delta);
+ }
+
+ mean = sum / ((uint64_t) count);
+ printf("all 1: mean:%lu sd:%f skipped:%lu\n",
+ mean,
+ sqrt((double)(sum_squares/((uint64_t) count) - mean*mean)),
+ skipped);
+
+ return 0;
+}
--- /dev/null
+.text
+.globl tsc_read
+
+tsc_read:
+rdtsc
+shl $32,%rdx
+or %rdx,%rax
+ret
--- /dev/null
+Ed25519
+=======
+
+This is a portable implementation of [Ed25519](http://ed25519.cr.yp.to/) based
+on the SUPERCOP "ref10" implementation. Additionally there is key exchanging
+and scalar addition included to further aid building a PKI using Ed25519. All
+code is in the public domain.
+
+All code is pure ANSI C without any dependencies, except for the random seed
+generation which uses standard OS cryptography APIs (`CryptGenRandom` on
+Windows, `/dev/urandom` on nix). If you wish to be entirely portable define
+`ED25519_NO_SEED`. This disables the `ed25519_create_seed` function, so if your
+application requires key generation you must supply your own seeding function
+(which is simply a 256 bit (32 byte) cryptographic random number generator).
+
+
+Performance
+-----------
+
+On a Windows machine with an Intel Pentium B970 @ 2.3GHz I got the following
+speeds (running on only one a single core):
+
+ Seed generation: 64us (15625 per second)
+ Key generation: 88us (11364 per second)
+ Message signing (short message): 87us (11494 per second)
+ Message verifying (short message): 228us (4386 per second)
+ Scalar addition: 100us (10000 per second)
+ Key exchange: 220us (4545 per second)
+
+The speeds on other machines may vary. Sign/verify times will be higher with
+longer messages. The implementation significantly benefits from 64 bit
+architectures, if possible compile as 64 bit.
+
+
+Usage
+-----
+
+Simply add all .c and .h files in the `src/` folder to your project and include
+`ed25519.h` in any file you want to use the API. If you prefer to use a shared
+library, only copy `ed25519.h` and define `ED25519_DLL` before importing. A
+windows DLL is pre-built.
+
+There are no defined types for seeds, private keys, public keys, shared secrets
+or signatures. Instead simple `unsigned char` buffers are used with the
+following sizes:
+
+```c
+unsigned char seed[32];
+unsigned char signature[64];
+unsigned char public_key[32];
+unsigned char private_key[64];
+unsigned char scalar[32];
+unsigned char shared_secret[32];
+```
+
+API
+---
+
+```c
+int ed25519_create_seed(unsigned char *seed);
+```
+
+Creates a 32 byte random seed in `seed` for key generation. `seed` must be a
+writable 32 byte buffer. Returns 0 on success, and nonzero on failure.
+
+```c
+void ed25519_create_keypair(unsigned char *public_key, unsigned char *private_key,
+ const unsigned char *seed);
+```
+
+Creates a new key pair from the given seed. `public_key` must be a writable 32
+byte buffer, `private_key` must be a writable 64 byte buffer and `seed` must be
+a 32 byte buffer.
+
+```c
+void ed25519_sign(unsigned char *signature,
+ const unsigned char *message, size_t message_len,
+ const unsigned char *public_key, const unsigned char *private_key);
+```
+
+Creates a signature of the given message with the given key pair. `signature`
+must be a writable 64 byte buffer. `message` must have at least `message_len`
+bytes to be read.
+
+```c
+int ed25519_verify(const unsigned char *signature,
+ const unsigned char *message, size_t message_len,
+ const unsigned char *public_key);
+```
+
+Verifies the signature on the given message using `public_key`. `signature`
+must be a readable 64 byte buffer. `message` must have at least `message_len`
+bytes to be read. Returns 1 if the signature matches, 0 otherwise.
+
+```c
+void ed25519_add_scalar(unsigned char *public_key, unsigned char *private_key,
+ const unsigned char *scalar);
+```
+
+Adds `scalar` to the given key pair where scalar is a 32 byte buffer (possibly
+generated with `ed25519_create_seed`), generating a new key pair. You can
+calculate the public key sum without knowing the private key and vice versa by
+passing in `NULL` for the key you don't know. This is useful for enforcing
+randomness on a key pair by a third party while only knowing the public key,
+among other things. Warning: the last bit of the scalar is ignored - if
+comparing scalars make sure to clear it with `scalar[31] &= 127`.
+
+
+```c
+void ed25519_key_exchange(unsigned char *shared_secret,
+ const unsigned char *public_key, const unsigned char *private_key);
+```
+
+Performs a key exchange on the given public key and private key, producing a
+shared secret. It is recommended to hash the shared secret before using it.
+`shared_secret` must be a 32 byte writable buffer where the shared secret will
+be stored.
+
+Example
+-------
+
+```c
+unsigned char seed[32], public_key[32], private_key[64], signature[64];
+unsigned char other_public_key[32], other_private_key[64], shared_secret[32];
+const unsigned char message[] = "TEST MESSAGE";
+
+/* create a random seed, and a key pair out of that seed */
+if (ed25519_create_seed(seed)) {
+ printf("error while generating seed\n");
+ exit(1);
+}
+
+ed25519_create_keypair(public_key, private_key, seed);
+
+/* create signature on the message with the key pair */
+ed25519_sign(signature, message, strlen(message), public_key, private_key);
+
+/* verify the signature */
+if (ed25519_verify(signature, message, strlen(message), public_key)) {
+ printf("valid signature\n");
+} else {
+ printf("invalid signature\n");
+}
+
+/* create a dummy keypair to use for a key exchange, normally you'd only have
+the public key and receive it through some communication channel */
+if (ed25519_create_seed(seed)) {
+ printf("error while generating seed\n");
+ exit(1);
+}
+
+ed25519_create_keypair(other_public_key, other_private_key, seed);
+
+/* do a key exchange with other_public_key */
+ed25519_key_exchange(shared_secret, other_public_key, private_key);
+
+/*
+ the magic here is that ed25519_key_exchange(shared_secret, public_key,
+ other_private_key); would result in the same shared_secret
+*/
+
+```
+
+License
+-------
+All code is in the public domain.
--- /dev/null
+#include "ed25519.h"
+#include "ge.h"
+#include "sc.h"
+
+
+/* see http://crypto.stackexchange.com/a/6215/4697 */
+void ed25519_add_scalar(unsigned char *public_key, unsigned char *private_key, const unsigned char *scalar) {
+ const unsigned char SC_1[32] = {1}; /* scalar with value 1 */
+
+ unsigned char n[32];
+ ge_p3 nB;
+ ge_p1p1 A_p1p1;
+ ge_p3 A;
+ ge_p3 public_key_unpacked;
+ ge_cached T;
+
+ int i;
+
+ /* copy the scalar and clear highest bit */
+ for (i = 0; i < 31; ++i) {
+ n[i] = scalar[i];
+ }
+ n[31] = scalar[31] & 127;
+
+ /* private key: a = n + t */
+ if (private_key) {
+ sc_muladd(private_key, SC_1, n, private_key);
+ }
+
+ /* public key: A = nB + T */
+ if (public_key) {
+ /* if we know the private key we don't need a point addition, which is faster */
+ /* using a "timing attack" you could find out wether or not we know the private
+ key, but this information seems rather useless - if this is important pass
+ public_key and private_key seperately in 2 function calls */
+ if (private_key) {
+ ge_scalarmult_base(&A, private_key);
+ } else {
+ /* unpack public key into T */
+ ge_frombytes_negate_vartime(&public_key_unpacked, public_key);
+ fe_neg(public_key_unpacked.X, public_key_unpacked.X); /* undo negate */
+ fe_neg(public_key_unpacked.T, public_key_unpacked.T); /* undo negate */
+ ge_p3_to_cached(&T, &public_key_unpacked);
+
+ /* calculate n*B */
+ ge_scalarmult_base(&nB, n);
+
+ /* A = n*B + T */
+ ge_add(&A_p1p1, &nB, &T);
+ ge_p1p1_to_p3(&A, &A_p1p1);
+ }
+
+ /* pack public key */
+ ge_p3_tobytes(public_key, &A);
+ }
+}
--- /dev/null
+#ifndef ED25519_H
+#define ED25519_H
+
+#include <stddef.h>
+
+#if defined(_WIN32)
+ #if defined(ED25519_BUILD_DLL)
+ #define ED25519_DECLSPEC __declspec(dllexport)
+ #elif defined(ED25519_DLL)
+ #define ED25519_DECLSPEC __declspec(dllimport)
+ #else
+ #define ED25519_DECLSPEC
+ #endif
+#else
+ #define ED25519_DECLSPEC
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef ED25519_NO_SEED
+int ED25519_DECLSPEC ed25519_create_seed(unsigned char *seed);
+#endif
+
+void ED25519_DECLSPEC ed25519_create_keypair(unsigned char *public_key, unsigned char *private_key, const unsigned char *seed);
+void ED25519_DECLSPEC ed25519_sign(unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key, const unsigned char *private_key);
+int ED25519_DECLSPEC ed25519_verify(const unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key);
+void ED25519_DECLSPEC ed25519_add_scalar(unsigned char *public_key, unsigned char *private_key, const unsigned char *scalar);
+void ED25519_DECLSPEC ed25519_key_exchange(unsigned char *shared_secret, const unsigned char *public_key, const unsigned char *private_key);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+#include "fixedint.h"
+#include "fe.h"
+
+#ifndef ED25519_LOAD_BYTES
+#define ED25519_LOAD_BYTES
+
+/*
+ helper functions
+*/
+static uint64_t load_3(const unsigned char *in) {
+ uint64_t result;
+
+ result = (uint64_t) in[0];
+ result |= ((uint64_t) in[1]) << 8;
+ result |= ((uint64_t) in[2]) << 16;
+
+ return result;
+}
+
+static uint64_t load_4(const unsigned char *in) {
+ uint64_t result;
+
+ result = (uint64_t) in[0];
+ result |= ((uint64_t) in[1]) << 8;
+ result |= ((uint64_t) in[2]) << 16;
+ result |= ((uint64_t) in[3]) << 24;
+
+ return result;
+}
+
+#endif
+
+/*
+ h = 0
+*/
+
+void fe_0(fe h) {
+ h[0] = 0;
+ h[1] = 0;
+ h[2] = 0;
+ h[3] = 0;
+ h[4] = 0;
+ h[5] = 0;
+ h[6] = 0;
+ h[7] = 0;
+ h[8] = 0;
+ h[9] = 0;
+}
+
+
+
+/*
+ h = 1
+*/
+
+void fe_1(fe h) {
+ h[0] = 1;
+ h[1] = 0;
+ h[2] = 0;
+ h[3] = 0;
+ h[4] = 0;
+ h[5] = 0;
+ h[6] = 0;
+ h[7] = 0;
+ h[8] = 0;
+ h[9] = 0;
+}
+
+
+
+/*
+ h = f + g
+ Can overlap h with f or g.
+
+ Preconditions:
+ |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+ |g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+
+ Postconditions:
+ |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
+*/
+
+void fe_add(fe h, const fe f, const fe g) {
+ int32_t f0 = f[0];
+ int32_t f1 = f[1];
+ int32_t f2 = f[2];
+ int32_t f3 = f[3];
+ int32_t f4 = f[4];
+ int32_t f5 = f[5];
+ int32_t f6 = f[6];
+ int32_t f7 = f[7];
+ int32_t f8 = f[8];
+ int32_t f9 = f[9];
+ int32_t g0 = g[0];
+ int32_t g1 = g[1];
+ int32_t g2 = g[2];
+ int32_t g3 = g[3];
+ int32_t g4 = g[4];
+ int32_t g5 = g[5];
+ int32_t g6 = g[6];
+ int32_t g7 = g[7];
+ int32_t g8 = g[8];
+ int32_t g9 = g[9];
+ int32_t h0 = f0 + g0;
+ int32_t h1 = f1 + g1;
+ int32_t h2 = f2 + g2;
+ int32_t h3 = f3 + g3;
+ int32_t h4 = f4 + g4;
+ int32_t h5 = f5 + g5;
+ int32_t h6 = f6 + g6;
+ int32_t h7 = f7 + g7;
+ int32_t h8 = f8 + g8;
+ int32_t h9 = f9 + g9;
+
+ h[0] = h0;
+ h[1] = h1;
+ h[2] = h2;
+ h[3] = h3;
+ h[4] = h4;
+ h[5] = h5;
+ h[6] = h6;
+ h[7] = h7;
+ h[8] = h8;
+ h[9] = h9;
+}
+
+
+
+/*
+ Replace (f,g) with (g,g) if b == 1;
+ replace (f,g) with (f,g) if b == 0.
+
+ Preconditions: b in {0,1}.
+*/
+
+void fe_cmov(fe f, const fe g, unsigned int b) {
+ int32_t f0 = f[0];
+ int32_t f1 = f[1];
+ int32_t f2 = f[2];
+ int32_t f3 = f[3];
+ int32_t f4 = f[4];
+ int32_t f5 = f[5];
+ int32_t f6 = f[6];
+ int32_t f7 = f[7];
+ int32_t f8 = f[8];
+ int32_t f9 = f[9];
+ int32_t g0 = g[0];
+ int32_t g1 = g[1];
+ int32_t g2 = g[2];
+ int32_t g3 = g[3];
+ int32_t g4 = g[4];
+ int32_t g5 = g[5];
+ int32_t g6 = g[6];
+ int32_t g7 = g[7];
+ int32_t g8 = g[8];
+ int32_t g9 = g[9];
+ int32_t x0 = f0 ^ g0;
+ int32_t x1 = f1 ^ g1;
+ int32_t x2 = f2 ^ g2;
+ int32_t x3 = f3 ^ g3;
+ int32_t x4 = f4 ^ g4;
+ int32_t x5 = f5 ^ g5;
+ int32_t x6 = f6 ^ g6;
+ int32_t x7 = f7 ^ g7;
+ int32_t x8 = f8 ^ g8;
+ int32_t x9 = f9 ^ g9;
+
+ b = (unsigned int) (- (int) b); /* silence warning */
+ x0 &= b;
+ x1 &= b;
+ x2 &= b;
+ x3 &= b;
+ x4 &= b;
+ x5 &= b;
+ x6 &= b;
+ x7 &= b;
+ x8 &= b;
+ x9 &= b;
+
+ f[0] = f0 ^ x0;
+ f[1] = f1 ^ x1;
+ f[2] = f2 ^ x2;
+ f[3] = f3 ^ x3;
+ f[4] = f4 ^ x4;
+ f[5] = f5 ^ x5;
+ f[6] = f6 ^ x6;
+ f[7] = f7 ^ x7;
+ f[8] = f8 ^ x8;
+ f[9] = f9 ^ x9;
+}
+
+/*
+ Replace (f,g) with (g,f) if b == 1;
+ replace (f,g) with (f,g) if b == 0.
+
+ Preconditions: b in {0,1}.
+*/
+
+void fe_cswap(fe f,fe g,unsigned int b) {
+ int32_t f0 = f[0];
+ int32_t f1 = f[1];
+ int32_t f2 = f[2];
+ int32_t f3 = f[3];
+ int32_t f4 = f[4];
+ int32_t f5 = f[5];
+ int32_t f6 = f[6];
+ int32_t f7 = f[7];
+ int32_t f8 = f[8];
+ int32_t f9 = f[9];
+ int32_t g0 = g[0];
+ int32_t g1 = g[1];
+ int32_t g2 = g[2];
+ int32_t g3 = g[3];
+ int32_t g4 = g[4];
+ int32_t g5 = g[5];
+ int32_t g6 = g[6];
+ int32_t g7 = g[7];
+ int32_t g8 = g[8];
+ int32_t g9 = g[9];
+ int32_t x0 = f0 ^ g0;
+ int32_t x1 = f1 ^ g1;
+ int32_t x2 = f2 ^ g2;
+ int32_t x3 = f3 ^ g3;
+ int32_t x4 = f4 ^ g4;
+ int32_t x5 = f5 ^ g5;
+ int32_t x6 = f6 ^ g6;
+ int32_t x7 = f7 ^ g7;
+ int32_t x8 = f8 ^ g8;
+ int32_t x9 = f9 ^ g9;
+ b = -b;
+ x0 &= b;
+ x1 &= b;
+ x2 &= b;
+ x3 &= b;
+ x4 &= b;
+ x5 &= b;
+ x6 &= b;
+ x7 &= b;
+ x8 &= b;
+ x9 &= b;
+ f[0] = f0 ^ x0;
+ f[1] = f1 ^ x1;
+ f[2] = f2 ^ x2;
+ f[3] = f3 ^ x3;
+ f[4] = f4 ^ x4;
+ f[5] = f5 ^ x5;
+ f[6] = f6 ^ x6;
+ f[7] = f7 ^ x7;
+ f[8] = f8 ^ x8;
+ f[9] = f9 ^ x9;
+ g[0] = g0 ^ x0;
+ g[1] = g1 ^ x1;
+ g[2] = g2 ^ x2;
+ g[3] = g3 ^ x3;
+ g[4] = g4 ^ x4;
+ g[5] = g5 ^ x5;
+ g[6] = g6 ^ x6;
+ g[7] = g7 ^ x7;
+ g[8] = g8 ^ x8;
+ g[9] = g9 ^ x9;
+}
+
+
+
+/*
+ h = f
+*/
+
+void fe_copy(fe h, const fe f) {
+ int32_t f0 = f[0];
+ int32_t f1 = f[1];
+ int32_t f2 = f[2];
+ int32_t f3 = f[3];
+ int32_t f4 = f[4];
+ int32_t f5 = f[5];
+ int32_t f6 = f[6];
+ int32_t f7 = f[7];
+ int32_t f8 = f[8];
+ int32_t f9 = f[9];
+
+ h[0] = f0;
+ h[1] = f1;
+ h[2] = f2;
+ h[3] = f3;
+ h[4] = f4;
+ h[5] = f5;
+ h[6] = f6;
+ h[7] = f7;
+ h[8] = f8;
+ h[9] = f9;
+}
+
+
+
+/*
+ Ignores top bit of h.
+*/
+
+void fe_frombytes(fe h, const unsigned char *s) {
+ int64_t h0 = load_4(s);
+ int64_t h1 = load_3(s + 4) << 6;
+ int64_t h2 = load_3(s + 7) << 5;
+ int64_t h3 = load_3(s + 10) << 3;
+ int64_t h4 = load_3(s + 13) << 2;
+ int64_t h5 = load_4(s + 16);
+ int64_t h6 = load_3(s + 20) << 7;
+ int64_t h7 = load_3(s + 23) << 5;
+ int64_t h8 = load_3(s + 26) << 4;
+ int64_t h9 = (load_3(s + 29) & 8388607) << 2;
+ int64_t carry0;
+ int64_t carry1;
+ int64_t carry2;
+ int64_t carry3;
+ int64_t carry4;
+ int64_t carry5;
+ int64_t carry6;
+ int64_t carry7;
+ int64_t carry8;
+ int64_t carry9;
+
+ carry9 = (h9 + (int64_t) (1 << 24)) >> 25;
+ h0 += carry9 * 19;
+ h9 -= carry9 << 25;
+ carry1 = (h1 + (int64_t) (1 << 24)) >> 25;
+ h2 += carry1;
+ h1 -= carry1 << 25;
+ carry3 = (h3 + (int64_t) (1 << 24)) >> 25;
+ h4 += carry3;
+ h3 -= carry3 << 25;
+ carry5 = (h5 + (int64_t) (1 << 24)) >> 25;
+ h6 += carry5;
+ h5 -= carry5 << 25;
+ carry7 = (h7 + (int64_t) (1 << 24)) >> 25;
+ h8 += carry7;
+ h7 -= carry7 << 25;
+ carry0 = (h0 + (int64_t) (1 << 25)) >> 26;
+ h1 += carry0;
+ h0 -= carry0 << 26;
+ carry2 = (h2 + (int64_t) (1 << 25)) >> 26;
+ h3 += carry2;
+ h2 -= carry2 << 26;
+ carry4 = (h4 + (int64_t) (1 << 25)) >> 26;
+ h5 += carry4;
+ h4 -= carry4 << 26;
+ carry6 = (h6 + (int64_t) (1 << 25)) >> 26;
+ h7 += carry6;
+ h6 -= carry6 << 26;
+ carry8 = (h8 + (int64_t) (1 << 25)) >> 26;
+ h9 += carry8;
+ h8 -= carry8 << 26;
+
+ h[0] = (int32_t) h0;
+ h[1] = (int32_t) h1;
+ h[2] = (int32_t) h2;
+ h[3] = (int32_t) h3;
+ h[4] = (int32_t) h4;
+ h[5] = (int32_t) h5;
+ h[6] = (int32_t) h6;
+ h[7] = (int32_t) h7;
+ h[8] = (int32_t) h8;
+ h[9] = (int32_t) h9;
+}
+
+
+
+void fe_invert(fe out, const fe z) {
+ fe t0;
+ fe t1;
+ fe t2;
+ fe t3;
+ int i;
+
+ fe_sq(t0, z);
+
+ for (i = 1; i < 1; ++i) {
+ fe_sq(t0, t0);
+ }
+
+ fe_sq(t1, t0);
+
+ for (i = 1; i < 2; ++i) {
+ fe_sq(t1, t1);
+ }
+
+ fe_mul(t1, z, t1);
+ fe_mul(t0, t0, t1);
+ fe_sq(t2, t0);
+
+ for (i = 1; i < 1; ++i) {
+ fe_sq(t2, t2);
+ }
+
+ fe_mul(t1, t1, t2);
+ fe_sq(t2, t1);
+
+ for (i = 1; i < 5; ++i) {
+ fe_sq(t2, t2);
+ }
+
+ fe_mul(t1, t2, t1);
+ fe_sq(t2, t1);
+
+ for (i = 1; i < 10; ++i) {
+ fe_sq(t2, t2);
+ }
+
+ fe_mul(t2, t2, t1);
+ fe_sq(t3, t2);
+
+ for (i = 1; i < 20; ++i) {
+ fe_sq(t3, t3);
+ }
+
+ fe_mul(t2, t3, t2);
+ fe_sq(t2, t2);
+
+ for (i = 1; i < 10; ++i) {
+ fe_sq(t2, t2);
+ }
+
+ fe_mul(t1, t2, t1);
+ fe_sq(t2, t1);
+
+ for (i = 1; i < 50; ++i) {
+ fe_sq(t2, t2);
+ }
+
+ fe_mul(t2, t2, t1);
+ fe_sq(t3, t2);
+
+ for (i = 1; i < 100; ++i) {
+ fe_sq(t3, t3);
+ }
+
+ fe_mul(t2, t3, t2);
+ fe_sq(t2, t2);
+
+ for (i = 1; i < 50; ++i) {
+ fe_sq(t2, t2);
+ }
+
+ fe_mul(t1, t2, t1);
+ fe_sq(t1, t1);
+
+ for (i = 1; i < 5; ++i) {
+ fe_sq(t1, t1);
+ }
+
+ fe_mul(out, t1, t0);
+}
+
+
+
+/*
+ return 1 if f is in {1,3,5,...,q-2}
+ return 0 if f is in {0,2,4,...,q-1}
+
+ Preconditions:
+ |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
+*/
+
+int fe_isnegative(const fe f) {
+ unsigned char s[32];
+
+ fe_tobytes(s, f);
+
+ return s[0] & 1;
+}
+
+
+
+/*
+ return 1 if f == 0
+ return 0 if f != 0
+
+ Preconditions:
+ |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
+*/
+
+int fe_isnonzero(const fe f) {
+ unsigned char s[32];
+ unsigned char r;
+
+ fe_tobytes(s, f);
+
+ r = s[0];
+ #define F(i) r |= s[i]
+ F(1);
+ F(2);
+ F(3);
+ F(4);
+ F(5);
+ F(6);
+ F(7);
+ F(8);
+ F(9);
+ F(10);
+ F(11);
+ F(12);
+ F(13);
+ F(14);
+ F(15);
+ F(16);
+ F(17);
+ F(18);
+ F(19);
+ F(20);
+ F(21);
+ F(22);
+ F(23);
+ F(24);
+ F(25);
+ F(26);
+ F(27);
+ F(28);
+ F(29);
+ F(30);
+ F(31);
+ #undef F
+
+ return r != 0;
+}
+
+
+
+/*
+ h = f * g
+ Can overlap h with f or g.
+
+ Preconditions:
+ |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.
+ |g| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.
+
+ Postconditions:
+ |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc.
+ */
+
+ /*
+ Notes on implementation strategy:
+
+ Using schoolbook multiplication.
+ Karatsuba would save a little in some cost models.
+
+ Most multiplications by 2 and 19 are 32-bit precomputations;
+ cheaper than 64-bit postcomputations.
+
+ There is one remaining multiplication by 19 in the carry chain;
+ one *19 precomputation can be merged into this,
+ but the resulting data flow is considerably less clean.
+
+ There are 12 carries below.
+ 10 of them are 2-way parallelizable and vectorizable.
+ Can get away with 11 carries, but then data flow is much deeper.
+
+ With tighter constraints on inputs can squeeze carries into int32.
+*/
+
+void fe_mul(fe h, const fe f, const fe g) {
+ int32_t f0 = f[0];
+ int32_t f1 = f[1];
+ int32_t f2 = f[2];
+ int32_t f3 = f[3];
+ int32_t f4 = f[4];
+ int32_t f5 = f[5];
+ int32_t f6 = f[6];
+ int32_t f7 = f[7];
+ int32_t f8 = f[8];
+ int32_t f9 = f[9];
+ int32_t g0 = g[0];
+ int32_t g1 = g[1];
+ int32_t g2 = g[2];
+ int32_t g3 = g[3];
+ int32_t g4 = g[4];
+ int32_t g5 = g[5];
+ int32_t g6 = g[6];
+ int32_t g7 = g[7];
+ int32_t g8 = g[8];
+ int32_t g9 = g[9];
+ int32_t g1_19 = 19 * g1; /* 1.959375*2^29 */
+ int32_t g2_19 = 19 * g2; /* 1.959375*2^30; still ok */
+ int32_t g3_19 = 19 * g3;
+ int32_t g4_19 = 19 * g4;
+ int32_t g5_19 = 19 * g5;
+ int32_t g6_19 = 19 * g6;
+ int32_t g7_19 = 19 * g7;
+ int32_t g8_19 = 19 * g8;
+ int32_t g9_19 = 19 * g9;
+ int32_t f1_2 = 2 * f1;
+ int32_t f3_2 = 2 * f3;
+ int32_t f5_2 = 2 * f5;
+ int32_t f7_2 = 2 * f7;
+ int32_t f9_2 = 2 * f9;
+ int64_t f0g0 = f0 * (int64_t) g0;
+ int64_t f0g1 = f0 * (int64_t) g1;
+ int64_t f0g2 = f0 * (int64_t) g2;
+ int64_t f0g3 = f0 * (int64_t) g3;
+ int64_t f0g4 = f0 * (int64_t) g4;
+ int64_t f0g5 = f0 * (int64_t) g5;
+ int64_t f0g6 = f0 * (int64_t) g6;
+ int64_t f0g7 = f0 * (int64_t) g7;
+ int64_t f0g8 = f0 * (int64_t) g8;
+ int64_t f0g9 = f0 * (int64_t) g9;
+ int64_t f1g0 = f1 * (int64_t) g0;
+ int64_t f1g1_2 = f1_2 * (int64_t) g1;
+ int64_t f1g2 = f1 * (int64_t) g2;
+ int64_t f1g3_2 = f1_2 * (int64_t) g3;
+ int64_t f1g4 = f1 * (int64_t) g4;
+ int64_t f1g5_2 = f1_2 * (int64_t) g5;
+ int64_t f1g6 = f1 * (int64_t) g6;
+ int64_t f1g7_2 = f1_2 * (int64_t) g7;
+ int64_t f1g8 = f1 * (int64_t) g8;
+ int64_t f1g9_38 = f1_2 * (int64_t) g9_19;
+ int64_t f2g0 = f2 * (int64_t) g0;
+ int64_t f2g1 = f2 * (int64_t) g1;
+ int64_t f2g2 = f2 * (int64_t) g2;
+ int64_t f2g3 = f2 * (int64_t) g3;
+ int64_t f2g4 = f2 * (int64_t) g4;
+ int64_t f2g5 = f2 * (int64_t) g5;
+ int64_t f2g6 = f2 * (int64_t) g6;
+ int64_t f2g7 = f2 * (int64_t) g7;
+ int64_t f2g8_19 = f2 * (int64_t) g8_19;
+ int64_t f2g9_19 = f2 * (int64_t) g9_19;
+ int64_t f3g0 = f3 * (int64_t) g0;
+ int64_t f3g1_2 = f3_2 * (int64_t) g1;
+ int64_t f3g2 = f3 * (int64_t) g2;
+ int64_t f3g3_2 = f3_2 * (int64_t) g3;
+ int64_t f3g4 = f3 * (int64_t) g4;
+ int64_t f3g5_2 = f3_2 * (int64_t) g5;
+ int64_t f3g6 = f3 * (int64_t) g6;
+ int64_t f3g7_38 = f3_2 * (int64_t) g7_19;
+ int64_t f3g8_19 = f3 * (int64_t) g8_19;
+ int64_t f3g9_38 = f3_2 * (int64_t) g9_19;
+ int64_t f4g0 = f4 * (int64_t) g0;
+ int64_t f4g1 = f4 * (int64_t) g1;
+ int64_t f4g2 = f4 * (int64_t) g2;
+ int64_t f4g3 = f4 * (int64_t) g3;
+ int64_t f4g4 = f4 * (int64_t) g4;
+ int64_t f4g5 = f4 * (int64_t) g5;
+ int64_t f4g6_19 = f4 * (int64_t) g6_19;
+ int64_t f4g7_19 = f4 * (int64_t) g7_19;
+ int64_t f4g8_19 = f4 * (int64_t) g8_19;
+ int64_t f4g9_19 = f4 * (int64_t) g9_19;
+ int64_t f5g0 = f5 * (int64_t) g0;
+ int64_t f5g1_2 = f5_2 * (int64_t) g1;
+ int64_t f5g2 = f5 * (int64_t) g2;
+ int64_t f5g3_2 = f5_2 * (int64_t) g3;
+ int64_t f5g4 = f5 * (int64_t) g4;
+ int64_t f5g5_38 = f5_2 * (int64_t) g5_19;
+ int64_t f5g6_19 = f5 * (int64_t) g6_19;
+ int64_t f5g7_38 = f5_2 * (int64_t) g7_19;
+ int64_t f5g8_19 = f5 * (int64_t) g8_19;
+ int64_t f5g9_38 = f5_2 * (int64_t) g9_19;
+ int64_t f6g0 = f6 * (int64_t) g0;
+ int64_t f6g1 = f6 * (int64_t) g1;
+ int64_t f6g2 = f6 * (int64_t) g2;
+ int64_t f6g3 = f6 * (int64_t) g3;
+ int64_t f6g4_19 = f6 * (int64_t) g4_19;
+ int64_t f6g5_19 = f6 * (int64_t) g5_19;
+ int64_t f6g6_19 = f6 * (int64_t) g6_19;
+ int64_t f6g7_19 = f6 * (int64_t) g7_19;
+ int64_t f6g8_19 = f6 * (int64_t) g8_19;
+ int64_t f6g9_19 = f6 * (int64_t) g9_19;
+ int64_t f7g0 = f7 * (int64_t) g0;
+ int64_t f7g1_2 = f7_2 * (int64_t) g1;
+ int64_t f7g2 = f7 * (int64_t) g2;
+ int64_t f7g3_38 = f7_2 * (int64_t) g3_19;
+ int64_t f7g4_19 = f7 * (int64_t) g4_19;
+ int64_t f7g5_38 = f7_2 * (int64_t) g5_19;
+ int64_t f7g6_19 = f7 * (int64_t) g6_19;
+ int64_t f7g7_38 = f7_2 * (int64_t) g7_19;
+ int64_t f7g8_19 = f7 * (int64_t) g8_19;
+ int64_t f7g9_38 = f7_2 * (int64_t) g9_19;
+ int64_t f8g0 = f8 * (int64_t) g0;
+ int64_t f8g1 = f8 * (int64_t) g1;
+ int64_t f8g2_19 = f8 * (int64_t) g2_19;
+ int64_t f8g3_19 = f8 * (int64_t) g3_19;
+ int64_t f8g4_19 = f8 * (int64_t) g4_19;
+ int64_t f8g5_19 = f8 * (int64_t) g5_19;
+ int64_t f8g6_19 = f8 * (int64_t) g6_19;
+ int64_t f8g7_19 = f8 * (int64_t) g7_19;
+ int64_t f8g8_19 = f8 * (int64_t) g8_19;
+ int64_t f8g9_19 = f8 * (int64_t) g9_19;
+ int64_t f9g0 = f9 * (int64_t) g0;
+ int64_t f9g1_38 = f9_2 * (int64_t) g1_19;
+ int64_t f9g2_19 = f9 * (int64_t) g2_19;
+ int64_t f9g3_38 = f9_2 * (int64_t) g3_19;
+ int64_t f9g4_19 = f9 * (int64_t) g4_19;
+ int64_t f9g5_38 = f9_2 * (int64_t) g5_19;
+ int64_t f9g6_19 = f9 * (int64_t) g6_19;
+ int64_t f9g7_38 = f9_2 * (int64_t) g7_19;
+ int64_t f9g8_19 = f9 * (int64_t) g8_19;
+ int64_t f9g9_38 = f9_2 * (int64_t) g9_19;
+ int64_t h0 = f0g0 + f1g9_38 + f2g8_19 + f3g7_38 + f4g6_19 + f5g5_38 + f6g4_19 + f7g3_38 + f8g2_19 + f9g1_38;
+ int64_t h1 = f0g1 + f1g0 + f2g9_19 + f3g8_19 + f4g7_19 + f5g6_19 + f6g5_19 + f7g4_19 + f8g3_19 + f9g2_19;
+ int64_t h2 = f0g2 + f1g1_2 + f2g0 + f3g9_38 + f4g8_19 + f5g7_38 + f6g6_19 + f7g5_38 + f8g4_19 + f9g3_38;
+ int64_t h3 = f0g3 + f1g2 + f2g1 + f3g0 + f4g9_19 + f5g8_19 + f6g7_19 + f7g6_19 + f8g5_19 + f9g4_19;
+ int64_t h4 = f0g4 + f1g3_2 + f2g2 + f3g1_2 + f4g0 + f5g9_38 + f6g8_19 + f7g7_38 + f8g6_19 + f9g5_38;
+ int64_t h5 = f0g5 + f1g4 + f2g3 + f3g2 + f4g1 + f5g0 + f6g9_19 + f7g8_19 + f8g7_19 + f9g6_19;
+ int64_t h6 = f0g6 + f1g5_2 + f2g4 + f3g3_2 + f4g2 + f5g1_2 + f6g0 + f7g9_38 + f8g8_19 + f9g7_38;
+ int64_t h7 = f0g7 + f1g6 + f2g5 + f3g4 + f4g3 + f5g2 + f6g1 + f7g0 + f8g9_19 + f9g8_19;
+ int64_t h8 = f0g8 + f1g7_2 + f2g6 + f3g5_2 + f4g4 + f5g3_2 + f6g2 + f7g1_2 + f8g0 + f9g9_38;
+ int64_t h9 = f0g9 + f1g8 + f2g7 + f3g6 + f4g5 + f5g4 + f6g3 + f7g2 + f8g1 + f9g0 ;
+ int64_t carry0;
+ int64_t carry1;
+ int64_t carry2;
+ int64_t carry3;
+ int64_t carry4;
+ int64_t carry5;
+ int64_t carry6;
+ int64_t carry7;
+ int64_t carry8;
+ int64_t carry9;
+
+ carry0 = (h0 + (int64_t) (1 << 25)) >> 26;
+ h1 += carry0;
+ h0 -= carry0 << 26;
+ carry4 = (h4 + (int64_t) (1 << 25)) >> 26;
+ h5 += carry4;
+ h4 -= carry4 << 26;
+
+ carry1 = (h1 + (int64_t) (1 << 24)) >> 25;
+ h2 += carry1;
+ h1 -= carry1 << 25;
+ carry5 = (h5 + (int64_t) (1 << 24)) >> 25;
+ h6 += carry5;
+ h5 -= carry5 << 25;
+
+ carry2 = (h2 + (int64_t) (1 << 25)) >> 26;
+ h3 += carry2;
+ h2 -= carry2 << 26;
+ carry6 = (h6 + (int64_t) (1 << 25)) >> 26;
+ h7 += carry6;
+ h6 -= carry6 << 26;
+
+ carry3 = (h3 + (int64_t) (1 << 24)) >> 25;
+ h4 += carry3;
+ h3 -= carry3 << 25;
+ carry7 = (h7 + (int64_t) (1 << 24)) >> 25;
+ h8 += carry7;
+ h7 -= carry7 << 25;
+
+ carry4 = (h4 + (int64_t) (1 << 25)) >> 26;
+ h5 += carry4;
+ h4 -= carry4 << 26;
+ carry8 = (h8 + (int64_t) (1 << 25)) >> 26;
+ h9 += carry8;
+ h8 -= carry8 << 26;
+
+ carry9 = (h9 + (int64_t) (1 << 24)) >> 25;
+ h0 += carry9 * 19;
+ h9 -= carry9 << 25;
+
+ carry0 = (h0 + (int64_t) (1 << 25)) >> 26;
+ h1 += carry0;
+ h0 -= carry0 << 26;
+
+ h[0] = (int32_t) h0;
+ h[1] = (int32_t) h1;
+ h[2] = (int32_t) h2;
+ h[3] = (int32_t) h3;
+ h[4] = (int32_t) h4;
+ h[5] = (int32_t) h5;
+ h[6] = (int32_t) h6;
+ h[7] = (int32_t) h7;
+ h[8] = (int32_t) h8;
+ h[9] = (int32_t) h9;
+}
+
+
+/*
+h = f * 121666
+Can overlap h with f.
+
+Preconditions:
+ |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
+
+Postconditions:
+ |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+*/
+
+void fe_mul121666(fe h, fe f) {
+ int32_t f0 = f[0];
+ int32_t f1 = f[1];
+ int32_t f2 = f[2];
+ int32_t f3 = f[3];
+ int32_t f4 = f[4];
+ int32_t f5 = f[5];
+ int32_t f6 = f[6];
+ int32_t f7 = f[7];
+ int32_t f8 = f[8];
+ int32_t f9 = f[9];
+ int64_t h0 = f0 * (int64_t) 121666;
+ int64_t h1 = f1 * (int64_t) 121666;
+ int64_t h2 = f2 * (int64_t) 121666;
+ int64_t h3 = f3 * (int64_t) 121666;
+ int64_t h4 = f4 * (int64_t) 121666;
+ int64_t h5 = f5 * (int64_t) 121666;
+ int64_t h6 = f6 * (int64_t) 121666;
+ int64_t h7 = f7 * (int64_t) 121666;
+ int64_t h8 = f8 * (int64_t) 121666;
+ int64_t h9 = f9 * (int64_t) 121666;
+ int64_t carry0;
+ int64_t carry1;
+ int64_t carry2;
+ int64_t carry3;
+ int64_t carry4;
+ int64_t carry5;
+ int64_t carry6;
+ int64_t carry7;
+ int64_t carry8;
+ int64_t carry9;
+
+ carry9 = (h9 + (int64_t) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25;
+ carry1 = (h1 + (int64_t) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25;
+ carry3 = (h3 + (int64_t) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25;
+ carry5 = (h5 + (int64_t) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25;
+ carry7 = (h7 + (int64_t) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25;
+
+ carry0 = (h0 + (int64_t) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26;
+ carry2 = (h2 + (int64_t) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26;
+ carry4 = (h4 + (int64_t) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26;
+ carry6 = (h6 + (int64_t) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26;
+ carry8 = (h8 + (int64_t) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26;
+
+ h[0] = h0;
+ h[1] = h1;
+ h[2] = h2;
+ h[3] = h3;
+ h[4] = h4;
+ h[5] = h5;
+ h[6] = h6;
+ h[7] = h7;
+ h[8] = h8;
+ h[9] = h9;
+}
+
+
+/*
+h = -f
+
+Preconditions:
+ |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+
+Postconditions:
+ |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+*/
+
+void fe_neg(fe h, const fe f) {
+ int32_t f0 = f[0];
+ int32_t f1 = f[1];
+ int32_t f2 = f[2];
+ int32_t f3 = f[3];
+ int32_t f4 = f[4];
+ int32_t f5 = f[5];
+ int32_t f6 = f[6];
+ int32_t f7 = f[7];
+ int32_t f8 = f[8];
+ int32_t f9 = f[9];
+ int32_t h0 = -f0;
+ int32_t h1 = -f1;
+ int32_t h2 = -f2;
+ int32_t h3 = -f3;
+ int32_t h4 = -f4;
+ int32_t h5 = -f5;
+ int32_t h6 = -f6;
+ int32_t h7 = -f7;
+ int32_t h8 = -f8;
+ int32_t h9 = -f9;
+
+ h[0] = h0;
+ h[1] = h1;
+ h[2] = h2;
+ h[3] = h3;
+ h[4] = h4;
+ h[5] = h5;
+ h[6] = h6;
+ h[7] = h7;
+ h[8] = h8;
+ h[9] = h9;
+}
+
+
+void fe_pow22523(fe out, const fe z) {
+ fe t0;
+ fe t1;
+ fe t2;
+ int i;
+ fe_sq(t0, z);
+
+ for (i = 1; i < 1; ++i) {
+ fe_sq(t0, t0);
+ }
+
+ fe_sq(t1, t0);
+
+ for (i = 1; i < 2; ++i) {
+ fe_sq(t1, t1);
+ }
+
+ fe_mul(t1, z, t1);
+ fe_mul(t0, t0, t1);
+ fe_sq(t0, t0);
+
+ for (i = 1; i < 1; ++i) {
+ fe_sq(t0, t0);
+ }
+
+ fe_mul(t0, t1, t0);
+ fe_sq(t1, t0);
+
+ for (i = 1; i < 5; ++i) {
+ fe_sq(t1, t1);
+ }
+
+ fe_mul(t0, t1, t0);
+ fe_sq(t1, t0);
+
+ for (i = 1; i < 10; ++i) {
+ fe_sq(t1, t1);
+ }
+
+ fe_mul(t1, t1, t0);
+ fe_sq(t2, t1);
+
+ for (i = 1; i < 20; ++i) {
+ fe_sq(t2, t2);
+ }
+
+ fe_mul(t1, t2, t1);
+ fe_sq(t1, t1);
+
+ for (i = 1; i < 10; ++i) {
+ fe_sq(t1, t1);
+ }
+
+ fe_mul(t0, t1, t0);
+ fe_sq(t1, t0);
+
+ for (i = 1; i < 50; ++i) {
+ fe_sq(t1, t1);
+ }
+
+ fe_mul(t1, t1, t0);
+ fe_sq(t2, t1);
+
+ for (i = 1; i < 100; ++i) {
+ fe_sq(t2, t2);
+ }
+
+ fe_mul(t1, t2, t1);
+ fe_sq(t1, t1);
+
+ for (i = 1; i < 50; ++i) {
+ fe_sq(t1, t1);
+ }
+
+ fe_mul(t0, t1, t0);
+ fe_sq(t0, t0);
+
+ for (i = 1; i < 2; ++i) {
+ fe_sq(t0, t0);
+ }
+
+ fe_mul(out, t0, z);
+ return;
+}
+
+
+/*
+h = f * f
+Can overlap h with f.
+
+Preconditions:
+ |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.
+
+Postconditions:
+ |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc.
+*/
+
+/*
+See fe_mul.c for discussion of implementation strategy.
+*/
+
+void fe_sq(fe h, const fe f) {
+ int32_t f0 = f[0];
+ int32_t f1 = f[1];
+ int32_t f2 = f[2];
+ int32_t f3 = f[3];
+ int32_t f4 = f[4];
+ int32_t f5 = f[5];
+ int32_t f6 = f[6];
+ int32_t f7 = f[7];
+ int32_t f8 = f[8];
+ int32_t f9 = f[9];
+ int32_t f0_2 = 2 * f0;
+ int32_t f1_2 = 2 * f1;
+ int32_t f2_2 = 2 * f2;
+ int32_t f3_2 = 2 * f3;
+ int32_t f4_2 = 2 * f4;
+ int32_t f5_2 = 2 * f5;
+ int32_t f6_2 = 2 * f6;
+ int32_t f7_2 = 2 * f7;
+ int32_t f5_38 = 38 * f5; /* 1.959375*2^30 */
+ int32_t f6_19 = 19 * f6; /* 1.959375*2^30 */
+ int32_t f7_38 = 38 * f7; /* 1.959375*2^30 */
+ int32_t f8_19 = 19 * f8; /* 1.959375*2^30 */
+ int32_t f9_38 = 38 * f9; /* 1.959375*2^30 */
+ int64_t f0f0 = f0 * (int64_t) f0;
+ int64_t f0f1_2 = f0_2 * (int64_t) f1;
+ int64_t f0f2_2 = f0_2 * (int64_t) f2;
+ int64_t f0f3_2 = f0_2 * (int64_t) f3;
+ int64_t f0f4_2 = f0_2 * (int64_t) f4;
+ int64_t f0f5_2 = f0_2 * (int64_t) f5;
+ int64_t f0f6_2 = f0_2 * (int64_t) f6;
+ int64_t f0f7_2 = f0_2 * (int64_t) f7;
+ int64_t f0f8_2 = f0_2 * (int64_t) f8;
+ int64_t f0f9_2 = f0_2 * (int64_t) f9;
+ int64_t f1f1_2 = f1_2 * (int64_t) f1;
+ int64_t f1f2_2 = f1_2 * (int64_t) f2;
+ int64_t f1f3_4 = f1_2 * (int64_t) f3_2;
+ int64_t f1f4_2 = f1_2 * (int64_t) f4;
+ int64_t f1f5_4 = f1_2 * (int64_t) f5_2;
+ int64_t f1f6_2 = f1_2 * (int64_t) f6;
+ int64_t f1f7_4 = f1_2 * (int64_t) f7_2;
+ int64_t f1f8_2 = f1_2 * (int64_t) f8;
+ int64_t f1f9_76 = f1_2 * (int64_t) f9_38;
+ int64_t f2f2 = f2 * (int64_t) f2;
+ int64_t f2f3_2 = f2_2 * (int64_t) f3;
+ int64_t f2f4_2 = f2_2 * (int64_t) f4;
+ int64_t f2f5_2 = f2_2 * (int64_t) f5;
+ int64_t f2f6_2 = f2_2 * (int64_t) f6;
+ int64_t f2f7_2 = f2_2 * (int64_t) f7;
+ int64_t f2f8_38 = f2_2 * (int64_t) f8_19;
+ int64_t f2f9_38 = f2 * (int64_t) f9_38;
+ int64_t f3f3_2 = f3_2 * (int64_t) f3;
+ int64_t f3f4_2 = f3_2 * (int64_t) f4;
+ int64_t f3f5_4 = f3_2 * (int64_t) f5_2;
+ int64_t f3f6_2 = f3_2 * (int64_t) f6;
+ int64_t f3f7_76 = f3_2 * (int64_t) f7_38;
+ int64_t f3f8_38 = f3_2 * (int64_t) f8_19;
+ int64_t f3f9_76 = f3_2 * (int64_t) f9_38;
+ int64_t f4f4 = f4 * (int64_t) f4;
+ int64_t f4f5_2 = f4_2 * (int64_t) f5;
+ int64_t f4f6_38 = f4_2 * (int64_t) f6_19;
+ int64_t f4f7_38 = f4 * (int64_t) f7_38;
+ int64_t f4f8_38 = f4_2 * (int64_t) f8_19;
+ int64_t f4f9_38 = f4 * (int64_t) f9_38;
+ int64_t f5f5_38 = f5 * (int64_t) f5_38;
+ int64_t f5f6_38 = f5_2 * (int64_t) f6_19;
+ int64_t f5f7_76 = f5_2 * (int64_t) f7_38;
+ int64_t f5f8_38 = f5_2 * (int64_t) f8_19;
+ int64_t f5f9_76 = f5_2 * (int64_t) f9_38;
+ int64_t f6f6_19 = f6 * (int64_t) f6_19;
+ int64_t f6f7_38 = f6 * (int64_t) f7_38;
+ int64_t f6f8_38 = f6_2 * (int64_t) f8_19;
+ int64_t f6f9_38 = f6 * (int64_t) f9_38;
+ int64_t f7f7_38 = f7 * (int64_t) f7_38;
+ int64_t f7f8_38 = f7_2 * (int64_t) f8_19;
+ int64_t f7f9_76 = f7_2 * (int64_t) f9_38;
+ int64_t f8f8_19 = f8 * (int64_t) f8_19;
+ int64_t f8f9_38 = f8 * (int64_t) f9_38;
+ int64_t f9f9_38 = f9 * (int64_t) f9_38;
+ int64_t h0 = f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38;
+ int64_t h1 = f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38;
+ int64_t h2 = f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19;
+ int64_t h3 = f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38;
+ int64_t h4 = f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38;
+ int64_t h5 = f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38;
+ int64_t h6 = f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19;
+ int64_t h7 = f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38;
+ int64_t h8 = f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38;
+ int64_t h9 = f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2;
+ int64_t carry0;
+ int64_t carry1;
+ int64_t carry2;
+ int64_t carry3;
+ int64_t carry4;
+ int64_t carry5;
+ int64_t carry6;
+ int64_t carry7;
+ int64_t carry8;
+ int64_t carry9;
+ carry0 = (h0 + (int64_t) (1 << 25)) >> 26;
+ h1 += carry0;
+ h0 -= carry0 << 26;
+ carry4 = (h4 + (int64_t) (1 << 25)) >> 26;
+ h5 += carry4;
+ h4 -= carry4 << 26;
+ carry1 = (h1 + (int64_t) (1 << 24)) >> 25;
+ h2 += carry1;
+ h1 -= carry1 << 25;
+ carry5 = (h5 + (int64_t) (1 << 24)) >> 25;
+ h6 += carry5;
+ h5 -= carry5 << 25;
+ carry2 = (h2 + (int64_t) (1 << 25)) >> 26;
+ h3 += carry2;
+ h2 -= carry2 << 26;
+ carry6 = (h6 + (int64_t) (1 << 25)) >> 26;
+ h7 += carry6;
+ h6 -= carry6 << 26;
+ carry3 = (h3 + (int64_t) (1 << 24)) >> 25;
+ h4 += carry3;
+ h3 -= carry3 << 25;
+ carry7 = (h7 + (int64_t) (1 << 24)) >> 25;
+ h8 += carry7;
+ h7 -= carry7 << 25;
+ carry4 = (h4 + (int64_t) (1 << 25)) >> 26;
+ h5 += carry4;
+ h4 -= carry4 << 26;
+ carry8 = (h8 + (int64_t) (1 << 25)) >> 26;
+ h9 += carry8;
+ h8 -= carry8 << 26;
+ carry9 = (h9 + (int64_t) (1 << 24)) >> 25;
+ h0 += carry9 * 19;
+ h9 -= carry9 << 25;
+ carry0 = (h0 + (int64_t) (1 << 25)) >> 26;
+ h1 += carry0;
+ h0 -= carry0 << 26;
+ h[0] = (int32_t) h0;
+ h[1] = (int32_t) h1;
+ h[2] = (int32_t) h2;
+ h[3] = (int32_t) h3;
+ h[4] = (int32_t) h4;
+ h[5] = (int32_t) h5;
+ h[6] = (int32_t) h6;
+ h[7] = (int32_t) h7;
+ h[8] = (int32_t) h8;
+ h[9] = (int32_t) h9;
+}
+
+
+/*
+h = 2 * f * f
+Can overlap h with f.
+
+Preconditions:
+ |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.
+
+Postconditions:
+ |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc.
+*/
+
+/*
+See fe_mul.c for discussion of implementation strategy.
+*/
+
+void fe_sq2(fe h, const fe f) {
+ int32_t f0 = f[0];
+ int32_t f1 = f[1];
+ int32_t f2 = f[2];
+ int32_t f3 = f[3];
+ int32_t f4 = f[4];
+ int32_t f5 = f[5];
+ int32_t f6 = f[6];
+ int32_t f7 = f[7];
+ int32_t f8 = f[8];
+ int32_t f9 = f[9];
+ int32_t f0_2 = 2 * f0;
+ int32_t f1_2 = 2 * f1;
+ int32_t f2_2 = 2 * f2;
+ int32_t f3_2 = 2 * f3;
+ int32_t f4_2 = 2 * f4;
+ int32_t f5_2 = 2 * f5;
+ int32_t f6_2 = 2 * f6;
+ int32_t f7_2 = 2 * f7;
+ int32_t f5_38 = 38 * f5; /* 1.959375*2^30 */
+ int32_t f6_19 = 19 * f6; /* 1.959375*2^30 */
+ int32_t f7_38 = 38 * f7; /* 1.959375*2^30 */
+ int32_t f8_19 = 19 * f8; /* 1.959375*2^30 */
+ int32_t f9_38 = 38 * f9; /* 1.959375*2^30 */
+ int64_t f0f0 = f0 * (int64_t) f0;
+ int64_t f0f1_2 = f0_2 * (int64_t) f1;
+ int64_t f0f2_2 = f0_2 * (int64_t) f2;
+ int64_t f0f3_2 = f0_2 * (int64_t) f3;
+ int64_t f0f4_2 = f0_2 * (int64_t) f4;
+ int64_t f0f5_2 = f0_2 * (int64_t) f5;
+ int64_t f0f6_2 = f0_2 * (int64_t) f6;
+ int64_t f0f7_2 = f0_2 * (int64_t) f7;
+ int64_t f0f8_2 = f0_2 * (int64_t) f8;
+ int64_t f0f9_2 = f0_2 * (int64_t) f9;
+ int64_t f1f1_2 = f1_2 * (int64_t) f1;
+ int64_t f1f2_2 = f1_2 * (int64_t) f2;
+ int64_t f1f3_4 = f1_2 * (int64_t) f3_2;
+ int64_t f1f4_2 = f1_2 * (int64_t) f4;
+ int64_t f1f5_4 = f1_2 * (int64_t) f5_2;
+ int64_t f1f6_2 = f1_2 * (int64_t) f6;
+ int64_t f1f7_4 = f1_2 * (int64_t) f7_2;
+ int64_t f1f8_2 = f1_2 * (int64_t) f8;
+ int64_t f1f9_76 = f1_2 * (int64_t) f9_38;
+ int64_t f2f2 = f2 * (int64_t) f2;
+ int64_t f2f3_2 = f2_2 * (int64_t) f3;
+ int64_t f2f4_2 = f2_2 * (int64_t) f4;
+ int64_t f2f5_2 = f2_2 * (int64_t) f5;
+ int64_t f2f6_2 = f2_2 * (int64_t) f6;
+ int64_t f2f7_2 = f2_2 * (int64_t) f7;
+ int64_t f2f8_38 = f2_2 * (int64_t) f8_19;
+ int64_t f2f9_38 = f2 * (int64_t) f9_38;
+ int64_t f3f3_2 = f3_2 * (int64_t) f3;
+ int64_t f3f4_2 = f3_2 * (int64_t) f4;
+ int64_t f3f5_4 = f3_2 * (int64_t) f5_2;
+ int64_t f3f6_2 = f3_2 * (int64_t) f6;
+ int64_t f3f7_76 = f3_2 * (int64_t) f7_38;
+ int64_t f3f8_38 = f3_2 * (int64_t) f8_19;
+ int64_t f3f9_76 = f3_2 * (int64_t) f9_38;
+ int64_t f4f4 = f4 * (int64_t) f4;
+ int64_t f4f5_2 = f4_2 * (int64_t) f5;
+ int64_t f4f6_38 = f4_2 * (int64_t) f6_19;
+ int64_t f4f7_38 = f4 * (int64_t) f7_38;
+ int64_t f4f8_38 = f4_2 * (int64_t) f8_19;
+ int64_t f4f9_38 = f4 * (int64_t) f9_38;
+ int64_t f5f5_38 = f5 * (int64_t) f5_38;
+ int64_t f5f6_38 = f5_2 * (int64_t) f6_19;
+ int64_t f5f7_76 = f5_2 * (int64_t) f7_38;
+ int64_t f5f8_38 = f5_2 * (int64_t) f8_19;
+ int64_t f5f9_76 = f5_2 * (int64_t) f9_38;
+ int64_t f6f6_19 = f6 * (int64_t) f6_19;
+ int64_t f6f7_38 = f6 * (int64_t) f7_38;
+ int64_t f6f8_38 = f6_2 * (int64_t) f8_19;
+ int64_t f6f9_38 = f6 * (int64_t) f9_38;
+ int64_t f7f7_38 = f7 * (int64_t) f7_38;
+ int64_t f7f8_38 = f7_2 * (int64_t) f8_19;
+ int64_t f7f9_76 = f7_2 * (int64_t) f9_38;
+ int64_t f8f8_19 = f8 * (int64_t) f8_19;
+ int64_t f8f9_38 = f8 * (int64_t) f9_38;
+ int64_t f9f9_38 = f9 * (int64_t) f9_38;
+ int64_t h0 = f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38;
+ int64_t h1 = f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38;
+ int64_t h2 = f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19;
+ int64_t h3 = f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38;
+ int64_t h4 = f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38;
+ int64_t h5 = f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38;
+ int64_t h6 = f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19;
+ int64_t h7 = f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38;
+ int64_t h8 = f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38;
+ int64_t h9 = f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2;
+ int64_t carry0;
+ int64_t carry1;
+ int64_t carry2;
+ int64_t carry3;
+ int64_t carry4;
+ int64_t carry5;
+ int64_t carry6;
+ int64_t carry7;
+ int64_t carry8;
+ int64_t carry9;
+ h0 += h0;
+ h1 += h1;
+ h2 += h2;
+ h3 += h3;
+ h4 += h4;
+ h5 += h5;
+ h6 += h6;
+ h7 += h7;
+ h8 += h8;
+ h9 += h9;
+ carry0 = (h0 + (int64_t) (1 << 25)) >> 26;
+ h1 += carry0;
+ h0 -= carry0 << 26;
+ carry4 = (h4 + (int64_t) (1 << 25)) >> 26;
+ h5 += carry4;
+ h4 -= carry4 << 26;
+ carry1 = (h1 + (int64_t) (1 << 24)) >> 25;
+ h2 += carry1;
+ h1 -= carry1 << 25;
+ carry5 = (h5 + (int64_t) (1 << 24)) >> 25;
+ h6 += carry5;
+ h5 -= carry5 << 25;
+ carry2 = (h2 + (int64_t) (1 << 25)) >> 26;
+ h3 += carry2;
+ h2 -= carry2 << 26;
+ carry6 = (h6 + (int64_t) (1 << 25)) >> 26;
+ h7 += carry6;
+ h6 -= carry6 << 26;
+ carry3 = (h3 + (int64_t) (1 << 24)) >> 25;
+ h4 += carry3;
+ h3 -= carry3 << 25;
+ carry7 = (h7 + (int64_t) (1 << 24)) >> 25;
+ h8 += carry7;
+ h7 -= carry7 << 25;
+ carry4 = (h4 + (int64_t) (1 << 25)) >> 26;
+ h5 += carry4;
+ h4 -= carry4 << 26;
+ carry8 = (h8 + (int64_t) (1 << 25)) >> 26;
+ h9 += carry8;
+ h8 -= carry8 << 26;
+ carry9 = (h9 + (int64_t) (1 << 24)) >> 25;
+ h0 += carry9 * 19;
+ h9 -= carry9 << 25;
+ carry0 = (h0 + (int64_t) (1 << 25)) >> 26;
+ h1 += carry0;
+ h0 -= carry0 << 26;
+ h[0] = (int32_t) h0;
+ h[1] = (int32_t) h1;
+ h[2] = (int32_t) h2;
+ h[3] = (int32_t) h3;
+ h[4] = (int32_t) h4;
+ h[5] = (int32_t) h5;
+ h[6] = (int32_t) h6;
+ h[7] = (int32_t) h7;
+ h[8] = (int32_t) h8;
+ h[9] = (int32_t) h9;
+}
+
+
+/*
+h = f - g
+Can overlap h with f or g.
+
+Preconditions:
+ |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+ |g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+
+Postconditions:
+ |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
+*/
+
+void fe_sub(fe h, const fe f, const fe g) {
+ int32_t f0 = f[0];
+ int32_t f1 = f[1];
+ int32_t f2 = f[2];
+ int32_t f3 = f[3];
+ int32_t f4 = f[4];
+ int32_t f5 = f[5];
+ int32_t f6 = f[6];
+ int32_t f7 = f[7];
+ int32_t f8 = f[8];
+ int32_t f9 = f[9];
+ int32_t g0 = g[0];
+ int32_t g1 = g[1];
+ int32_t g2 = g[2];
+ int32_t g3 = g[3];
+ int32_t g4 = g[4];
+ int32_t g5 = g[5];
+ int32_t g6 = g[6];
+ int32_t g7 = g[7];
+ int32_t g8 = g[8];
+ int32_t g9 = g[9];
+ int32_t h0 = f0 - g0;
+ int32_t h1 = f1 - g1;
+ int32_t h2 = f2 - g2;
+ int32_t h3 = f3 - g3;
+ int32_t h4 = f4 - g4;
+ int32_t h5 = f5 - g5;
+ int32_t h6 = f6 - g6;
+ int32_t h7 = f7 - g7;
+ int32_t h8 = f8 - g8;
+ int32_t h9 = f9 - g9;
+
+ h[0] = h0;
+ h[1] = h1;
+ h[2] = h2;
+ h[3] = h3;
+ h[4] = h4;
+ h[5] = h5;
+ h[6] = h6;
+ h[7] = h7;
+ h[8] = h8;
+ h[9] = h9;
+}
+
+
+
+/*
+Preconditions:
+ |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
+
+Write p=2^255-19; q=floor(h/p).
+Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))).
+
+Proof:
+ Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4.
+ Also have |h-2^230 h9|<2^231 so |19 2^(-255)(h-2^230 h9)|<1/4.
+
+ Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9).
+ Then 0<y<1.
+
+ Write r=h-pq.
+ Have 0<=r<=p-1=2^255-20.
+ Thus 0<=r+19(2^-255)r<r+19(2^-255)2^255<=2^255-1.
+
+ Write x=r+19(2^-255)r+y.
+ Then 0<x<2^255 so floor(2^(-255)x) = 0 so floor(q+2^(-255)x) = q.
+
+ Have q+2^(-255)x = 2^(-255)(h + 19 2^(-25) h9 + 2^(-1))
+ so floor(2^(-255)(h + 19 2^(-25) h9 + 2^(-1))) = q.
+*/
+
+void fe_tobytes(unsigned char *s, const fe h) {
+ int32_t h0 = h[0];
+ int32_t h1 = h[1];
+ int32_t h2 = h[2];
+ int32_t h3 = h[3];
+ int32_t h4 = h[4];
+ int32_t h5 = h[5];
+ int32_t h6 = h[6];
+ int32_t h7 = h[7];
+ int32_t h8 = h[8];
+ int32_t h9 = h[9];
+ int32_t q;
+ int32_t carry0;
+ int32_t carry1;
+ int32_t carry2;
+ int32_t carry3;
+ int32_t carry4;
+ int32_t carry5;
+ int32_t carry6;
+ int32_t carry7;
+ int32_t carry8;
+ int32_t carry9;
+ q = (19 * h9 + (((int32_t) 1) << 24)) >> 25;
+ q = (h0 + q) >> 26;
+ q = (h1 + q) >> 25;
+ q = (h2 + q) >> 26;
+ q = (h3 + q) >> 25;
+ q = (h4 + q) >> 26;
+ q = (h5 + q) >> 25;
+ q = (h6 + q) >> 26;
+ q = (h7 + q) >> 25;
+ q = (h8 + q) >> 26;
+ q = (h9 + q) >> 25;
+ /* Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20. */
+ h0 += 19 * q;
+ /* Goal: Output h-2^255 q, which is between 0 and 2^255-20. */
+ carry0 = h0 >> 26;
+ h1 += carry0;
+ h0 -= carry0 << 26;
+ carry1 = h1 >> 25;
+ h2 += carry1;
+ h1 -= carry1 << 25;
+ carry2 = h2 >> 26;
+ h3 += carry2;
+ h2 -= carry2 << 26;
+ carry3 = h3 >> 25;
+ h4 += carry3;
+ h3 -= carry3 << 25;
+ carry4 = h4 >> 26;
+ h5 += carry4;
+ h4 -= carry4 << 26;
+ carry5 = h5 >> 25;
+ h6 += carry5;
+ h5 -= carry5 << 25;
+ carry6 = h6 >> 26;
+ h7 += carry6;
+ h6 -= carry6 << 26;
+ carry7 = h7 >> 25;
+ h8 += carry7;
+ h7 -= carry7 << 25;
+ carry8 = h8 >> 26;
+ h9 += carry8;
+ h8 -= carry8 << 26;
+ carry9 = h9 >> 25;
+ h9 -= carry9 << 25;
+
+ /* h10 = carry9 */
+ /*
+ Goal: Output h0+...+2^255 h10-2^255 q, which is between 0 and 2^255-20.
+ Have h0+...+2^230 h9 between 0 and 2^255-1;
+ evidently 2^255 h10-2^255 q = 0.
+ Goal: Output h0+...+2^230 h9.
+ */
+ s[0] = (unsigned char) (h0 >> 0);
+ s[1] = (unsigned char) (h0 >> 8);
+ s[2] = (unsigned char) (h0 >> 16);
+ s[3] = (unsigned char) ((h0 >> 24) | (h1 << 2));
+ s[4] = (unsigned char) (h1 >> 6);
+ s[5] = (unsigned char) (h1 >> 14);
+ s[6] = (unsigned char) ((h1 >> 22) | (h2 << 3));
+ s[7] = (unsigned char) (h2 >> 5);
+ s[8] = (unsigned char) (h2 >> 13);
+ s[9] = (unsigned char) ((h2 >> 21) | (h3 << 5));
+ s[10] = (unsigned char) (h3 >> 3);
+ s[11] = (unsigned char) (h3 >> 11);
+ s[12] = (unsigned char) ((h3 >> 19) | (h4 << 6));
+ s[13] = (unsigned char) (h4 >> 2);
+ s[14] = (unsigned char) (h4 >> 10);
+ s[15] = (unsigned char) (h4 >> 18);
+ s[16] = (unsigned char) (h5 >> 0);
+ s[17] = (unsigned char) (h5 >> 8);
+ s[18] = (unsigned char) (h5 >> 16);
+ s[19] = (unsigned char) ((h5 >> 24) | (h6 << 1));
+ s[20] = (unsigned char) (h6 >> 7);
+ s[21] = (unsigned char) (h6 >> 15);
+ s[22] = (unsigned char) ((h6 >> 23) | (h7 << 3));
+ s[23] = (unsigned char) (h7 >> 5);
+ s[24] = (unsigned char) (h7 >> 13);
+ s[25] = (unsigned char) ((h7 >> 21) | (h8 << 4));
+ s[26] = (unsigned char) (h8 >> 4);
+ s[27] = (unsigned char) (h8 >> 12);
+ s[28] = (unsigned char) ((h8 >> 20) | (h9 << 6));
+ s[29] = (unsigned char) (h9 >> 2);
+ s[30] = (unsigned char) (h9 >> 10);
+ s[31] = (unsigned char) (h9 >> 18);
+}
--- /dev/null
+#ifndef FE_H
+#define FE_H
+
+#include "fixedint.h"
+
+
+/*
+ fe means field element.
+ Here the field is \Z/(2^255-19).
+ An element t, entries t[0]...t[9], represents the integer
+ t[0]+2^26 t[1]+2^51 t[2]+2^77 t[3]+2^102 t[4]+...+2^230 t[9].
+ Bounds on each t[i] vary depending on context.
+*/
+
+
+typedef int32_t fe[10];
+
+
+void fe_0(fe h);
+void fe_1(fe h);
+
+void fe_frombytes(fe h, const unsigned char *s);
+void fe_tobytes(unsigned char *s, const fe h);
+
+void fe_copy(fe h, const fe f);
+int fe_isnegative(const fe f);
+int fe_isnonzero(const fe f);
+void fe_cmov(fe f, const fe g, unsigned int b);
+void fe_cswap(fe f, fe g, unsigned int b);
+
+void fe_neg(fe h, const fe f);
+void fe_add(fe h, const fe f, const fe g);
+void fe_invert(fe out, const fe z);
+void fe_sq(fe h, const fe f);
+void fe_sq2(fe h, const fe f);
+void fe_mul(fe h, const fe f, const fe g);
+void fe_mul121666(fe h, fe f);
+void fe_pow22523(fe out, const fe z);
+void fe_sub(fe h, const fe f, const fe g);
+
+#endif
--- /dev/null
+/*
+ Portable header to provide the 32 and 64 bits type.
+
+ Not a compatible replacement for <stdint.h>, do not blindly use it as such.
+*/
+
+#if ((defined(__STDC__) && __STDC__ && __STDC_VERSION__ >= 199901L) || (defined(__WATCOMC__) && (defined(_STDINT_H_INCLUDED) || __WATCOMC__ >= 1250)) || (defined(__GNUC__) && (defined(_STDINT_H) || defined(_STDINT_H_) || defined(__UINT_FAST64_TYPE__)) )) && !defined(FIXEDINT_H_INCLUDED)
+ #include <stdint.h>
+ #define FIXEDINT_H_INCLUDED
+
+ #if defined(__WATCOMC__) && __WATCOMC__ >= 1250 && !defined(UINT64_C)
+ #include <limits.h>
+ #define UINT64_C(x) (x + (UINT64_MAX - UINT64_MAX))
+ #endif
+#endif
+
+
+#ifndef FIXEDINT_H_INCLUDED
+ #define FIXEDINT_H_INCLUDED
+
+ #include <limits.h>
+
+ /* (u)int32_t */
+ #ifndef uint32_t
+ #if (ULONG_MAX == 0xffffffffUL)
+ typedef unsigned long uint32_t;
+ #elif (UINT_MAX == 0xffffffffUL)
+ typedef unsigned int uint32_t;
+ #elif (USHRT_MAX == 0xffffffffUL)
+ typedef unsigned short uint32_t;
+ #endif
+ #endif
+
+
+ #ifndef int32_t
+ #if (LONG_MAX == 0x7fffffffL)
+ typedef signed long int32_t;
+ #elif (INT_MAX == 0x7fffffffL)
+ typedef signed int int32_t;
+ #elif (SHRT_MAX == 0x7fffffffL)
+ typedef signed short int32_t;
+ #endif
+ #endif
+
+
+ /* (u)int64_t */
+ #if (defined(__STDC__) && defined(__STDC_VERSION__) && __STDC__ && __STDC_VERSION__ >= 199901L)
+ typedef long long int64_t;
+ typedef unsigned long long uint64_t;
+
+ #define UINT64_C(v) v ##ULL
+ #define INT64_C(v) v ##LL
+ #elif defined(__GNUC__)
+ __extension__ typedef long long int64_t;
+ __extension__ typedef unsigned long long uint64_t;
+
+ #define UINT64_C(v) v ##ULL
+ #define INT64_C(v) v ##LL
+ #elif defined(__MWERKS__) || defined(__SUNPRO_C) || defined(__SUNPRO_CC) || defined(__APPLE_CC__) || defined(_LONG_LONG) || defined(_CRAYC)
+ typedef long long int64_t;
+ typedef unsigned long long uint64_t;
+
+ #define UINT64_C(v) v ##ULL
+ #define INT64_C(v) v ##LL
+ #elif (defined(__WATCOMC__) && defined(__WATCOM_INT64__)) || (defined(_MSC_VER) && _INTEGRAL_MAX_BITS >= 64) || (defined(__BORLANDC__) && __BORLANDC__ > 0x460) || defined(__alpha) || defined(__DECC)
+ typedef __int64 int64_t;
+ typedef unsigned __int64 uint64_t;
+
+ #define UINT64_C(v) v ##UI64
+ #define INT64_C(v) v ##I64
+ #endif
+#endif
--- /dev/null
+#include "ge.h"
+#include "precomp_data.h"
+
+
+/*
+r = p + q
+*/
+
+void ge_add(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) {
+ fe t0;
+ fe_add(r->X, p->Y, p->X);
+ fe_sub(r->Y, p->Y, p->X);
+ fe_mul(r->Z, r->X, q->YplusX);
+ fe_mul(r->Y, r->Y, q->YminusX);
+ fe_mul(r->T, q->T2d, p->T);
+ fe_mul(r->X, p->Z, q->Z);
+ fe_add(t0, r->X, r->X);
+ fe_sub(r->X, r->Z, r->Y);
+ fe_add(r->Y, r->Z, r->Y);
+ fe_add(r->Z, t0, r->T);
+ fe_sub(r->T, t0, r->T);
+}
+
+
+static void slide(signed char *r, const unsigned char *a) {
+ int i;
+ int b;
+ int k;
+
+ for (i = 0; i < 256; ++i) {
+ r[i] = 1 & (a[i >> 3] >> (i & 7));
+ }
+
+ for (i = 0; i < 256; ++i)
+ if (r[i]) {
+ for (b = 1; b <= 6 && i + b < 256; ++b) {
+ if (r[i + b]) {
+ if (r[i] + (r[i + b] << b) <= 15) {
+ r[i] += r[i + b] << b;
+ r[i + b] = 0;
+ } else if (r[i] - (r[i + b] << b) >= -15) {
+ r[i] -= r[i + b] << b;
+
+ for (k = i + b; k < 256; ++k) {
+ if (!r[k]) {
+ r[k] = 1;
+ break;
+ }
+
+ r[k] = 0;
+ }
+ } else {
+ break;
+ }
+ }
+ }
+ }
+}
+
+/*
+r = a * A + b * B
+where a = a[0]+256*a[1]+...+256^31 a[31].
+and b = b[0]+256*b[1]+...+256^31 b[31].
+B is the Ed25519 base point (x,4/5) with x positive.
+*/
+
+void ge_double_scalarmult_vartime(ge_p2 *r, const unsigned char *a, const ge_p3 *A, const unsigned char *b) {
+ signed char aslide[256];
+ signed char bslide[256];
+ ge_cached Ai[8]; /* A,3A,5A,7A,9A,11A,13A,15A */
+ ge_p1p1 t;
+ ge_p3 u;
+ ge_p3 A2;
+ int i;
+ slide(aslide, a);
+ slide(bslide, b);
+ ge_p3_to_cached(&Ai[0], A);
+ ge_p3_dbl(&t, A);
+ ge_p1p1_to_p3(&A2, &t);
+ ge_add(&t, &A2, &Ai[0]);
+ ge_p1p1_to_p3(&u, &t);
+ ge_p3_to_cached(&Ai[1], &u);
+ ge_add(&t, &A2, &Ai[1]);
+ ge_p1p1_to_p3(&u, &t);
+ ge_p3_to_cached(&Ai[2], &u);
+ ge_add(&t, &A2, &Ai[2]);
+ ge_p1p1_to_p3(&u, &t);
+ ge_p3_to_cached(&Ai[3], &u);
+ ge_add(&t, &A2, &Ai[3]);
+ ge_p1p1_to_p3(&u, &t);
+ ge_p3_to_cached(&Ai[4], &u);
+ ge_add(&t, &A2, &Ai[4]);
+ ge_p1p1_to_p3(&u, &t);
+ ge_p3_to_cached(&Ai[5], &u);
+ ge_add(&t, &A2, &Ai[5]);
+ ge_p1p1_to_p3(&u, &t);
+ ge_p3_to_cached(&Ai[6], &u);
+ ge_add(&t, &A2, &Ai[6]);
+ ge_p1p1_to_p3(&u, &t);
+ ge_p3_to_cached(&Ai[7], &u);
+ ge_p2_0(r);
+
+ for (i = 255; i >= 0; --i) {
+ if (aslide[i] || bslide[i]) {
+ break;
+ }
+ }
+
+ for (; i >= 0; --i) {
+ ge_p2_dbl(&t, r);
+
+ if (aslide[i] > 0) {
+ ge_p1p1_to_p3(&u, &t);
+ ge_add(&t, &u, &Ai[aslide[i] / 2]);
+ } else if (aslide[i] < 0) {
+ ge_p1p1_to_p3(&u, &t);
+ ge_sub(&t, &u, &Ai[(-aslide[i]) / 2]);
+ }
+
+ if (bslide[i] > 0) {
+ ge_p1p1_to_p3(&u, &t);
+ ge_madd(&t, &u, &Bi[bslide[i] / 2]);
+ } else if (bslide[i] < 0) {
+ ge_p1p1_to_p3(&u, &t);
+ ge_msub(&t, &u, &Bi[(-bslide[i]) / 2]);
+ }
+
+ ge_p1p1_to_p2(r, &t);
+ }
+}
+
+
+static const fe d = {
+ -10913610, 13857413, -15372611, 6949391, 114729, -8787816, -6275908, -3247719, -18696448, -12055116
+};
+
+static const fe sqrtm1 = {
+ -32595792, -7943725, 9377950, 3500415, 12389472, -272473, -25146209, -2005654, 326686, 11406482
+};
+
+int ge_frombytes_negate_vartime(ge_p3 *h, const unsigned char *s) {
+ fe u;
+ fe v;
+ fe v3;
+ fe vxx;
+ fe check;
+ fe_frombytes(h->Y, s);
+ fe_1(h->Z);
+ fe_sq(u, h->Y);
+ fe_mul(v, u, d);
+ fe_sub(u, u, h->Z); /* u = y^2-1 */
+ fe_add(v, v, h->Z); /* v = dy^2+1 */
+ fe_sq(v3, v);
+ fe_mul(v3, v3, v); /* v3 = v^3 */
+ fe_sq(h->X, v3);
+ fe_mul(h->X, h->X, v);
+ fe_mul(h->X, h->X, u); /* x = uv^7 */
+ fe_pow22523(h->X, h->X); /* x = (uv^7)^((q-5)/8) */
+ fe_mul(h->X, h->X, v3);
+ fe_mul(h->X, h->X, u); /* x = uv^3(uv^7)^((q-5)/8) */
+ fe_sq(vxx, h->X);
+ fe_mul(vxx, vxx, v);
+ fe_sub(check, vxx, u); /* vx^2-u */
+
+ if (fe_isnonzero(check)) {
+ fe_add(check, vxx, u); /* vx^2+u */
+
+ if (fe_isnonzero(check)) {
+ return -1;
+ }
+
+ fe_mul(h->X, h->X, sqrtm1);
+ }
+
+ if (fe_isnegative(h->X) == (s[31] >> 7)) {
+ fe_neg(h->X, h->X);
+ }
+
+ fe_mul(h->T, h->X, h->Y);
+ return 0;
+}
+
+
+/*
+r = p + q
+*/
+
+void ge_madd(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q) {
+ fe t0;
+ fe_add(r->X, p->Y, p->X);
+ fe_sub(r->Y, p->Y, p->X);
+ fe_mul(r->Z, r->X, q->yplusx);
+ fe_mul(r->Y, r->Y, q->yminusx);
+ fe_mul(r->T, q->xy2d, p->T);
+ fe_add(t0, p->Z, p->Z);
+ fe_sub(r->X, r->Z, r->Y);
+ fe_add(r->Y, r->Z, r->Y);
+ fe_add(r->Z, t0, r->T);
+ fe_sub(r->T, t0, r->T);
+}
+
+
+/*
+r = p - q
+*/
+
+void ge_msub(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q) {
+ fe t0;
+
+ fe_add(r->X, p->Y, p->X);
+ fe_sub(r->Y, p->Y, p->X);
+ fe_mul(r->Z, r->X, q->yminusx);
+ fe_mul(r->Y, r->Y, q->yplusx);
+ fe_mul(r->T, q->xy2d, p->T);
+ fe_add(t0, p->Z, p->Z);
+ fe_sub(r->X, r->Z, r->Y);
+ fe_add(r->Y, r->Z, r->Y);
+ fe_sub(r->Z, t0, r->T);
+ fe_add(r->T, t0, r->T);
+}
+
+
+/*
+r = p
+*/
+
+void ge_p1p1_to_p2(ge_p2 *r, const ge_p1p1 *p) {
+ fe_mul(r->X, p->X, p->T);
+ fe_mul(r->Y, p->Y, p->Z);
+ fe_mul(r->Z, p->Z, p->T);
+}
+
+
+
+/*
+r = p
+*/
+
+void ge_p1p1_to_p3(ge_p3 *r, const ge_p1p1 *p) {
+ fe_mul(r->X, p->X, p->T);
+ fe_mul(r->Y, p->Y, p->Z);
+ fe_mul(r->Z, p->Z, p->T);
+ fe_mul(r->T, p->X, p->Y);
+}
+
+
+void ge_p2_0(ge_p2 *h) {
+ fe_0(h->X);
+ fe_1(h->Y);
+ fe_1(h->Z);
+}
+
+
+
+/*
+r = 2 * p
+*/
+
+void ge_p2_dbl(ge_p1p1 *r, const ge_p2 *p) {
+ fe t0;
+
+ fe_sq(r->X, p->X);
+ fe_sq(r->Z, p->Y);
+ fe_sq2(r->T, p->Z);
+ fe_add(r->Y, p->X, p->Y);
+ fe_sq(t0, r->Y);
+ fe_add(r->Y, r->Z, r->X);
+ fe_sub(r->Z, r->Z, r->X);
+ fe_sub(r->X, t0, r->Y);
+ fe_sub(r->T, r->T, r->Z);
+}
+
+
+void ge_p3_0(ge_p3 *h) {
+ fe_0(h->X);
+ fe_1(h->Y);
+ fe_1(h->Z);
+ fe_0(h->T);
+}
+
+
+/*
+r = 2 * p
+*/
+
+void ge_p3_dbl(ge_p1p1 *r, const ge_p3 *p) {
+ ge_p2 q;
+ ge_p3_to_p2(&q, p);
+ ge_p2_dbl(r, &q);
+}
+
+
+
+/*
+r = p
+*/
+
+static const fe d2 = {
+ -21827239, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199
+};
+
+void ge_p3_to_cached(ge_cached *r, const ge_p3 *p) {
+ fe_add(r->YplusX, p->Y, p->X);
+ fe_sub(r->YminusX, p->Y, p->X);
+ fe_copy(r->Z, p->Z);
+ fe_mul(r->T2d, p->T, d2);
+}
+
+
+/*
+r = p
+*/
+
+void ge_p3_to_p2(ge_p2 *r, const ge_p3 *p) {
+ fe_copy(r->X, p->X);
+ fe_copy(r->Y, p->Y);
+ fe_copy(r->Z, p->Z);
+}
+
+
+void ge_p3_tobytes(unsigned char *s, const ge_p3 *h) {
+ fe recip;
+ fe x;
+ fe y;
+ fe_invert(recip, h->Z);
+ fe_mul(x, h->X, recip);
+ fe_mul(y, h->Y, recip);
+ fe_tobytes(s, y);
+ s[31] ^= fe_isnegative(x) << 7;
+}
+
+
+static unsigned char equal(signed char b, signed char c) {
+ unsigned char ub = b;
+ unsigned char uc = c;
+ unsigned char x = ub ^ uc; /* 0: yes; 1..255: no */
+ uint64_t y = x; /* 0: yes; 1..255: no */
+ y -= 1; /* large: yes; 0..254: no */
+ y >>= 63; /* 1: yes; 0: no */
+ return (unsigned char) y;
+}
+
+static unsigned char negative(signed char b) {
+ uint64_t x = b; /* 18446744073709551361..18446744073709551615: yes; 0..255: no */
+ x >>= 63; /* 1: yes; 0: no */
+ return (unsigned char) x;
+}
+
+static void cmov(ge_precomp *t, ge_precomp *u, unsigned char b) {
+ fe_cmov(t->yplusx, u->yplusx, b);
+ fe_cmov(t->yminusx, u->yminusx, b);
+ fe_cmov(t->xy2d, u->xy2d, b);
+}
+
+
+static void select(ge_precomp *t, int pos, signed char b) {
+ ge_precomp minust;
+ unsigned char bnegative = negative(b);
+ unsigned char babs = b - (((-bnegative) & b) << 1);
+ fe_1(t->yplusx);
+ fe_1(t->yminusx);
+ fe_0(t->xy2d);
+ cmov(t, &base[pos][0], equal(babs, 1));
+ cmov(t, &base[pos][1], equal(babs, 2));
+ cmov(t, &base[pos][2], equal(babs, 3));
+ cmov(t, &base[pos][3], equal(babs, 4));
+ cmov(t, &base[pos][4], equal(babs, 5));
+ cmov(t, &base[pos][5], equal(babs, 6));
+ cmov(t, &base[pos][6], equal(babs, 7));
+ cmov(t, &base[pos][7], equal(babs, 8));
+ fe_copy(minust.yplusx, t->yminusx);
+ fe_copy(minust.yminusx, t->yplusx);
+ fe_neg(minust.xy2d, t->xy2d);
+ cmov(t, &minust, bnegative);
+}
+
+/*
+h = a * B
+where a = a[0]+256*a[1]+...+256^31 a[31]
+B is the Ed25519 base point (x,4/5) with x positive.
+
+Preconditions:
+ a[31] <= 127
+*/
+
+void ge_scalarmult_base(ge_p3 *h, const unsigned char *a) {
+ signed char e[64];
+ signed char carry;
+ ge_p1p1 r;
+ ge_p2 s;
+ ge_precomp t;
+ int i;
+
+ for (i = 0; i < 32; ++i) {
+ e[2 * i + 0] = (a[i] >> 0) & 15;
+ e[2 * i + 1] = (a[i] >> 4) & 15;
+ }
+
+ /* each e[i] is between 0 and 15 */
+ /* e[63] is between 0 and 7 */
+ carry = 0;
+
+ for (i = 0; i < 63; ++i) {
+ e[i] += carry;
+ carry = e[i] + 8;
+ carry >>= 4;
+ e[i] -= carry << 4;
+ }
+
+ e[63] += carry;
+ /* each e[i] is between -8 and 8 */
+ ge_p3_0(h);
+
+ for (i = 1; i < 64; i += 2) {
+ select(&t, i / 2, e[i]);
+ ge_madd(&r, h, &t);
+ ge_p1p1_to_p3(h, &r);
+ }
+
+ ge_p3_dbl(&r, h);
+ ge_p1p1_to_p2(&s, &r);
+ ge_p2_dbl(&r, &s);
+ ge_p1p1_to_p2(&s, &r);
+ ge_p2_dbl(&r, &s);
+ ge_p1p1_to_p2(&s, &r);
+ ge_p2_dbl(&r, &s);
+ ge_p1p1_to_p3(h, &r);
+
+ for (i = 0; i < 64; i += 2) {
+ select(&t, i / 2, e[i]);
+ ge_madd(&r, h, &t);
+ ge_p1p1_to_p3(h, &r);
+ }
+}
+
+
+/*
+r = p - q
+*/
+
+void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) {
+ fe t0;
+
+ fe_add(r->X, p->Y, p->X);
+ fe_sub(r->Y, p->Y, p->X);
+ fe_mul(r->Z, r->X, q->YminusX);
+ fe_mul(r->Y, r->Y, q->YplusX);
+ fe_mul(r->T, q->T2d, p->T);
+ fe_mul(r->X, p->Z, q->Z);
+ fe_add(t0, r->X, r->X);
+ fe_sub(r->X, r->Z, r->Y);
+ fe_add(r->Y, r->Z, r->Y);
+ fe_sub(r->Z, t0, r->T);
+ fe_add(r->T, t0, r->T);
+}
+
+
+void ge_tobytes(unsigned char *s, const ge_p2 *h) {
+ fe recip;
+ fe x;
+ fe y;
+ fe_invert(recip, h->Z);
+ fe_mul(x, h->X, recip);
+ fe_mul(y, h->Y, recip);
+ fe_tobytes(s, y);
+ s[31] ^= fe_isnegative(x) << 7;
+}
--- /dev/null
+#ifndef GE_H
+#define GE_H
+
+#include "fe.h"
+
+
+/*
+ge means group element.
+
+Here the group is the set of pairs (x,y) of field elements (see fe.h)
+satisfying -x^2 + y^2 = 1 + d x^2y^2
+where d = -121665/121666.
+
+Representations:
+ ge_p2 (projective): (X:Y:Z) satisfying x=X/Z, y=Y/Z
+ ge_p3 (extended): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT
+ ge_p1p1 (completed): ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T
+ ge_precomp (Duif): (y+x,y-x,2dxy)
+*/
+
+typedef struct {
+ fe X;
+ fe Y;
+ fe Z;
+} ge_p2;
+
+typedef struct {
+ fe X;
+ fe Y;
+ fe Z;
+ fe T;
+} ge_p3;
+
+typedef struct {
+ fe X;
+ fe Y;
+ fe Z;
+ fe T;
+} ge_p1p1;
+
+typedef struct {
+ fe yplusx;
+ fe yminusx;
+ fe xy2d;
+} ge_precomp;
+
+typedef struct {
+ fe YplusX;
+ fe YminusX;
+ fe Z;
+ fe T2d;
+} ge_cached;
+
+void ge_p3_tobytes(unsigned char *s, const ge_p3 *h);
+void ge_tobytes(unsigned char *s, const ge_p2 *h);
+int ge_frombytes_negate_vartime(ge_p3 *h, const unsigned char *s);
+
+void ge_add(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q);
+void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q);
+void ge_double_scalarmult_vartime(ge_p2 *r, const unsigned char *a, const ge_p3 *A, const unsigned char *b);
+void ge_madd(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q);
+void ge_msub(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q);
+void ge_scalarmult_base(ge_p3 *h, const unsigned char *a);
+
+void ge_p1p1_to_p2(ge_p2 *r, const ge_p1p1 *p);
+void ge_p1p1_to_p3(ge_p3 *r, const ge_p1p1 *p);
+void ge_p2_0(ge_p2 *h);
+void ge_p2_dbl(ge_p1p1 *r, const ge_p2 *p);
+void ge_p3_0(ge_p3 *h);
+void ge_p3_dbl(ge_p1p1 *r, const ge_p3 *p);
+void ge_p3_to_cached(ge_cached *r, const ge_p3 *p);
+void ge_p3_to_p2(ge_p2 *r, const ge_p3 *p);
+
+#endif
--- /dev/null
+#include "ed25519.h"
+#include "fe.h"
+
+void ed25519_key_exchange(unsigned char *shared_secret, const unsigned char *public_key, const unsigned char *private_key) {
+ unsigned char e[32];
+ unsigned int i;
+
+ fe x1;
+ fe x2;
+ fe z2;
+ fe x3;
+ fe z3;
+ fe tmp0;
+ fe tmp1;
+
+ int pos;
+ unsigned int swap;
+ unsigned int b;
+
+ /* copy the private key and make sure it's valid */
+ for (i = 0; i < 32; ++i) {
+ e[i] = private_key[i];
+ }
+
+ e[0] &= 248;
+ e[31] &= 63;
+ e[31] |= 64;
+
+ /* unpack the public key and convert edwards to montgomery */
+ /* due to CodesInChaos: montgomeryX = (edwardsY + 1)*inverse(1 - edwardsY) mod p */
+ fe_frombytes(x1, public_key);
+ fe_1(tmp1);
+ fe_add(tmp0, x1, tmp1);
+ fe_sub(tmp1, tmp1, x1);
+ fe_invert(tmp1, tmp1);
+ fe_mul(x1, tmp0, tmp1);
+
+ fe_1(x2);
+ fe_0(z2);
+ fe_copy(x3, x1);
+ fe_1(z3);
+
+ swap = 0;
+ for (pos = 254; pos >= 0; --pos) {
+ b = e[pos / 8] >> (pos & 7);
+ b &= 1;
+ swap ^= b;
+ fe_cswap(x2, x3, swap);
+ fe_cswap(z2, z3, swap);
+ swap = b;
+
+ /* from montgomery.h */
+ fe_sub(tmp0, x3, z3);
+ fe_sub(tmp1, x2, z2);
+ fe_add(x2, x2, z2);
+ fe_add(z2, x3, z3);
+ fe_mul(z3, tmp0, x2);
+ fe_mul(z2, z2, tmp1);
+ fe_sq(tmp0, tmp1);
+ fe_sq(tmp1, x2);
+ fe_add(x3, z3, z2);
+ fe_sub(z2, z3, z2);
+ fe_mul(x2, tmp1, tmp0);
+ fe_sub(tmp1, tmp1, tmp0);
+ fe_sq(z2, z2);
+ fe_mul121666(z3, tmp1);
+ fe_sq(x3, x3);
+ fe_add(tmp0, tmp0, z3);
+ fe_mul(z3, x1, z2);
+ fe_mul(z2, tmp1, tmp0);
+ }
+
+ fe_cswap(x2, x3, swap);
+ fe_cswap(z2, z3, swap);
+
+ fe_invert(z2, z2);
+ fe_mul(x2, x2, z2);
+ fe_tobytes(shared_secret, x2);
+}
--- /dev/null
+#include "ed25519.h"
+#include "sha512.h"
+#include "ge.h"
+
+
+void ed25519_create_keypair(unsigned char *public_key, unsigned char *private_key, const unsigned char *seed) {
+ ge_p3 A;
+
+ sha512(seed, 32, private_key);
+ private_key[0] &= 248;
+ private_key[31] &= 63;
+ private_key[31] |= 64;
+
+ ge_scalarmult_base(&A, private_key);
+ ge_p3_tobytes(public_key, &A);
+}
--- /dev/null
+static ge_precomp Bi[8] = {
+ {
+ { 25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605 },
+ { -12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378 },
+ { -8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546 },
+ },
+ {
+ { 15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024 },
+ { 16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574 },
+ { 30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357 },
+ },
+ {
+ { 10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380 },
+ { 4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306 },
+ { 19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942 },
+ },
+ {
+ { 5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766 },
+ { -30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701 },
+ { 28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300 },
+ },
+ {
+ { -22518993, -6692182, 14201702, -8745502, -23510406, 8844726, 18474211, -1361450, -13062696, 13821877 },
+ { -6455177, -7839871, 3374702, -4740862, -27098617, -10571707, 31655028, -7212327, 18853322, -14220951 },
+ { 4566830, -12963868, -28974889, -12240689, -7602672, -2830569, -8514358, -10431137, 2207753, -3209784 },
+ },
+ {
+ { -25154831, -4185821, 29681144, 7868801, -6854661, -9423865, -12437364, -663000, -31111463, -16132436 },
+ { 25576264, -2703214, 7349804, -11814844, 16472782, 9300885, 3844789, 15725684, 171356, 6466918 },
+ { 23103977, 13316479, 9739013, -16149481, 817875, -15038942, 8965339, -14088058, -30714912, 16193877 },
+ },
+ {
+ { -33521811, 3180713, -2394130, 14003687, -16903474, -16270840, 17238398, 4729455, -18074513, 9256800 },
+ { -25182317, -4174131, 32336398, 5036987, -21236817, 11360617, 22616405, 9761698, -19827198, 630305 },
+ { -13720693, 2639453, -24237460, -7406481, 9494427, -5774029, -6554551, -15960994, -2449256, -14291300 },
+ },
+ {
+ { -3151181, -5046075, 9282714, 6866145, -31907062, -863023, -18940575, 15033784, 25105118, -7894876 },
+ { -24326370, 15950226, -31801215, -14592823, -11662737, -5090925, 1573892, -2625887, 2198790, -15804619 },
+ { -3099351, 10324967, -2241613, 7453183, -5446979, -2735503, -13812022, -16236442, -32461234, -12290683 },
+ },
+};
+
+
+/* base[i][j] = (j+1)*256^i*B */
+static ge_precomp base[32][8] = {
+ {
+ {
+ { 25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605 },
+ { -12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378 },
+ { -8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546 },
+ },
+ {
+ { -12815894, -12976347, -21581243, 11784320, -25355658, -2750717, -11717903, -3814571, -358445, -10211303 },
+ { -21703237, 6903825, 27185491, 6451973, -29577724, -9554005, -15616551, 11189268, -26829678, -5319081 },
+ { 26966642, 11152617, 32442495, 15396054, 14353839, -12752335, -3128826, -9541118, -15472047, -4166697 },
+ },
+ {
+ { 15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024 },
+ { 16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574 },
+ { 30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357 },
+ },
+ {
+ { -17036878, 13921892, 10945806, -6033431, 27105052, -16084379, -28926210, 15006023, 3284568, -6276540 },
+ { 23599295, -8306047, -11193664, -7687416, 13236774, 10506355, 7464579, 9656445, 13059162, 10374397 },
+ { 7798556, 16710257, 3033922, 2874086, 28997861, 2835604, 32406664, -3839045, -641708, -101325 },
+ },
+ {
+ { 10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380 },
+ { 4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306 },
+ { 19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942 },
+ },
+ {
+ { -15371964, -12862754, 32573250, 4720197, -26436522, 5875511, -19188627, -15224819, -9818940, -12085777 },
+ { -8549212, 109983, 15149363, 2178705, 22900618, 4543417, 3044240, -15689887, 1762328, 14866737 },
+ { -18199695, -15951423, -10473290, 1707278, -17185920, 3916101, -28236412, 3959421, 27914454, 4383652 },
+ },
+ {
+ { 5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766 },
+ { -30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701 },
+ { 28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300 },
+ },
+ {
+ { 14499471, -2729599, -33191113, -4254652, 28494862, 14271267, 30290735, 10876454, -33154098, 2381726 },
+ { -7195431, -2655363, -14730155, 462251, -27724326, 3941372, -6236617, 3696005, -32300832, 15351955 },
+ { 27431194, 8222322, 16448760, -3907995, -18707002, 11938355, -32961401, -2970515, 29551813, 10109425 },
+ },
+ },
+ {
+ {
+ { -13657040, -13155431, -31283750, 11777098, 21447386, 6519384, -2378284, -1627556, 10092783, -4764171 },
+ { 27939166, 14210322, 4677035, 16277044, -22964462, -12398139, -32508754, 12005538, -17810127, 12803510 },
+ { 17228999, -15661624, -1233527, 300140, -1224870, -11714777, 30364213, -9038194, 18016357, 4397660 },
+ },
+ {
+ { -10958843, -7690207, 4776341, -14954238, 27850028, -15602212, -26619106, 14544525, -17477504, 982639 },
+ { 29253598, 15796703, -2863982, -9908884, 10057023, 3163536, 7332899, -4120128, -21047696, 9934963 },
+ { 5793303, 16271923, -24131614, -10116404, 29188560, 1206517, -14747930, 4559895, -30123922, -10897950 },
+ },
+ {
+ { -27643952, -11493006, 16282657, -11036493, 28414021, -15012264, 24191034, 4541697, -13338309, 5500568 },
+ { 12650548, -1497113, 9052871, 11355358, -17680037, -8400164, -17430592, 12264343, 10874051, 13524335 },
+ { 25556948, -3045990, 714651, 2510400, 23394682, -10415330, 33119038, 5080568, -22528059, 5376628 },
+ },
+ {
+ { -26088264, -4011052, -17013699, -3537628, -6726793, 1920897, -22321305, -9447443, 4535768, 1569007 },
+ { -2255422, 14606630, -21692440, -8039818, 28430649, 8775819, -30494562, 3044290, 31848280, 12543772 },
+ { -22028579, 2943893, -31857513, 6777306, 13784462, -4292203, -27377195, -2062731, 7718482, 14474653 },
+ },
+ {
+ { 2385315, 2454213, -22631320, 46603, -4437935, -15680415, 656965, -7236665, 24316168, -5253567 },
+ { 13741529, 10911568, -33233417, -8603737, -20177830, -1033297, 33040651, -13424532, -20729456, 8321686 },
+ { 21060490, -2212744, 15712757, -4336099, 1639040, 10656336, 23845965, -11874838, -9984458, 608372 },
+ },
+ {
+ { -13672732, -15087586, -10889693, -7557059, -6036909, 11305547, 1123968, -6780577, 27229399, 23887 },
+ { -23244140, -294205, -11744728, 14712571, -29465699, -2029617, 12797024, -6440308, -1633405, 16678954 },
+ { -29500620, 4770662, -16054387, 14001338, 7830047, 9564805, -1508144, -4795045, -17169265, 4904953 },
+ },
+ {
+ { 24059557, 14617003, 19037157, -15039908, 19766093, -14906429, 5169211, 16191880, 2128236, -4326833 },
+ { -16981152, 4124966, -8540610, -10653797, 30336522, -14105247, -29806336, 916033, -6882542, -2986532 },
+ { -22630907, 12419372, -7134229, -7473371, -16478904, 16739175, 285431, 2763829, 15736322, 4143876 },
+ },
+ {
+ { 2379352, 11839345, -4110402, -5988665, 11274298, 794957, 212801, -14594663, 23527084, -16458268 },
+ { 33431127, -11130478, -17838966, -15626900, 8909499, 8376530, -32625340, 4087881, -15188911, -14416214 },
+ { 1767683, 7197987, -13205226, -2022635, -13091350, 448826, 5799055, 4357868, -4774191, -16323038 },
+ },
+ },
+ {
+ {
+ { 6721966, 13833823, -23523388, -1551314, 26354293, -11863321, 23365147, -3949732, 7390890, 2759800 },
+ { 4409041, 2052381, 23373853, 10530217, 7676779, -12885954, 21302353, -4264057, 1244380, -12919645 },
+ { -4421239, 7169619, 4982368, -2957590, 30256825, -2777540, 14086413, 9208236, 15886429, 16489664 },
+ },
+ {
+ { 1996075, 10375649, 14346367, 13311202, -6874135, -16438411, -13693198, 398369, -30606455, -712933 },
+ { -25307465, 9795880, -2777414, 14878809, -33531835, 14780363, 13348553, 12076947, -30836462, 5113182 },
+ { -17770784, 11797796, 31950843, 13929123, -25888302, 12288344, -30341101, -7336386, 13847711, 5387222 },
+ },
+ {
+ { -18582163, -3416217, 17824843, -2340966, 22744343, -10442611, 8763061, 3617786, -19600662, 10370991 },
+ { 20246567, -14369378, 22358229, -543712, 18507283, -10413996, 14554437, -8746092, 32232924, 16763880 },
+ { 9648505, 10094563, 26416693, 14745928, -30374318, -6472621, 11094161, 15689506, 3140038, -16510092 },
+ },
+ {
+ { -16160072, 5472695, 31895588, 4744994, 8823515, 10365685, -27224800, 9448613, -28774454, 366295 },
+ { 19153450, 11523972, -11096490, -6503142, -24647631, 5420647, 28344573, 8041113, 719605, 11671788 },
+ { 8678025, 2694440, -6808014, 2517372, 4964326, 11152271, -15432916, -15266516, 27000813, -10195553 },
+ },
+ {
+ { -15157904, 7134312, 8639287, -2814877, -7235688, 10421742, 564065, 5336097, 6750977, -14521026 },
+ { 11836410, -3979488, 26297894, 16080799, 23455045, 15735944, 1695823, -8819122, 8169720, 16220347 },
+ { -18115838, 8653647, 17578566, -6092619, -8025777, -16012763, -11144307, -2627664, -5990708, -14166033 },
+ },
+ {
+ { -23308498, -10968312, 15213228, -10081214, -30853605, -11050004, 27884329, 2847284, 2655861, 1738395 },
+ { -27537433, -14253021, -25336301, -8002780, -9370762, 8129821, 21651608, -3239336, -19087449, -11005278 },
+ { 1533110, 3437855, 23735889, 459276, 29970501, 11335377, 26030092, 5821408, 10478196, 8544890 },
+ },
+ {
+ { 32173121, -16129311, 24896207, 3921497, 22579056, -3410854, 19270449, 12217473, 17789017, -3395995 },
+ { -30552961, -2228401, -15578829, -10147201, 13243889, 517024, 15479401, -3853233, 30460520, 1052596 },
+ { -11614875, 13323618, 32618793, 8175907, -15230173, 12596687, 27491595, -4612359, 3179268, -9478891 },
+ },
+ {
+ { 31947069, -14366651, -4640583, -15339921, -15125977, -6039709, -14756777, -16411740, 19072640, -9511060 },
+ { 11685058, 11822410, 3158003, -13952594, 33402194, -4165066, 5977896, -5215017, 473099, 5040608 },
+ { -20290863, 8198642, -27410132, 11602123, 1290375, -2799760, 28326862, 1721092, -19558642, -3131606 },
+ },
+ },
+ {
+ {
+ { 7881532, 10687937, 7578723, 7738378, -18951012, -2553952, 21820786, 8076149, -27868496, 11538389 },
+ { -19935666, 3899861, 18283497, -6801568, -15728660, -11249211, 8754525, 7446702, -5676054, 5797016 },
+ { -11295600, -3793569, -15782110, -7964573, 12708869, -8456199, 2014099, -9050574, -2369172, -5877341 },
+ },
+ {
+ { -22472376, -11568741, -27682020, 1146375, 18956691, 16640559, 1192730, -3714199, 15123619, 10811505 },
+ { 14352098, -3419715, -18942044, 10822655, 32750596, 4699007, -70363, 15776356, -28886779, -11974553 },
+ { -28241164, -8072475, -4978962, -5315317, 29416931, 1847569, -20654173, -16484855, 4714547, -9600655 },
+ },
+ {
+ { 15200332, 8368572, 19679101, 15970074, -31872674, 1959451, 24611599, -4543832, -11745876, 12340220 },
+ { 12876937, -10480056, 33134381, 6590940, -6307776, 14872440, 9613953, 8241152, 15370987, 9608631 },
+ { -4143277, -12014408, 8446281, -391603, 4407738, 13629032, -7724868, 15866074, -28210621, -8814099 },
+ },
+ {
+ { 26660628, -15677655, 8393734, 358047, -7401291, 992988, -23904233, 858697, 20571223, 8420556 },
+ { 14620715, 13067227, -15447274, 8264467, 14106269, 15080814, 33531827, 12516406, -21574435, -12476749 },
+ { 236881, 10476226, 57258, -14677024, 6472998, 2466984, 17258519, 7256740, 8791136, 15069930 },
+ },
+ {
+ { 1276410, -9371918, 22949635, -16322807, -23493039, -5702186, 14711875, 4874229, -30663140, -2331391 },
+ { 5855666, 4990204, -13711848, 7294284, -7804282, 1924647, -1423175, -7912378, -33069337, 9234253 },
+ { 20590503, -9018988, 31529744, -7352666, -2706834, 10650548, 31559055, -11609587, 18979186, 13396066 },
+ },
+ {
+ { 24474287, 4968103, 22267082, 4407354, 24063882, -8325180, -18816887, 13594782, 33514650, 7021958 },
+ { -11566906, -6565505, -21365085, 15928892, -26158305, 4315421, -25948728, -3916677, -21480480, 12868082 },
+ { -28635013, 13504661, 19988037, -2132761, 21078225, 6443208, -21446107, 2244500, -12455797, -8089383 },
+ },
+ {
+ { -30595528, 13793479, -5852820, 319136, -25723172, -6263899, 33086546, 8957937, -15233648, 5540521 },
+ { -11630176, -11503902, -8119500, -7643073, 2620056, 1022908, -23710744, -1568984, -16128528, -14962807 },
+ { 23152971, 775386, 27395463, 14006635, -9701118, 4649512, 1689819, 892185, -11513277, -15205948 },
+ },
+ {
+ { 9770129, 9586738, 26496094, 4324120, 1556511, -3550024, 27453819, 4763127, -19179614, 5867134 },
+ { -32765025, 1927590, 31726409, -4753295, 23962434, -16019500, 27846559, 5931263, -29749703, -16108455 },
+ { 27461885, -2977536, 22380810, 1815854, -23033753, -3031938, 7283490, -15148073, -19526700, 7734629 },
+ },
+ },
+ {
+ {
+ { -8010264, -9590817, -11120403, 6196038, 29344158, -13430885, 7585295, -3176626, 18549497, 15302069 },
+ { -32658337, -6171222, -7672793, -11051681, 6258878, 13504381, 10458790, -6418461, -8872242, 8424746 },
+ { 24687205, 8613276, -30667046, -3233545, 1863892, -1830544, 19206234, 7134917, -11284482, -828919 },
+ },
+ {
+ { 11334899, -9218022, 8025293, 12707519, 17523892, -10476071, 10243738, -14685461, -5066034, 16498837 },
+ { 8911542, 6887158, -9584260, -6958590, 11145641, -9543680, 17303925, -14124238, 6536641, 10543906 },
+ { -28946384, 15479763, -17466835, 568876, -1497683, 11223454, -2669190, -16625574, -27235709, 8876771 },
+ },
+ {
+ { -25742899, -12566864, -15649966, -846607, -33026686, -796288, -33481822, 15824474, -604426, -9039817 },
+ { 10330056, 70051, 7957388, -9002667, 9764902, 15609756, 27698697, -4890037, 1657394, 3084098 },
+ { 10477963, -7470260, 12119566, -13250805, 29016247, -5365589, 31280319, 14396151, -30233575, 15272409 },
+ },
+ {
+ { -12288309, 3169463, 28813183, 16658753, 25116432, -5630466, -25173957, -12636138, -25014757, 1950504 },
+ { -26180358, 9489187, 11053416, -14746161, -31053720, 5825630, -8384306, -8767532, 15341279, 8373727 },
+ { 28685821, 7759505, -14378516, -12002860, -31971820, 4079242, 298136, -10232602, -2878207, 15190420 },
+ },
+ {
+ { -32932876, 13806336, -14337485, -15794431, -24004620, 10940928, 8669718, 2742393, -26033313, -6875003 },
+ { -1580388, -11729417, -25979658, -11445023, -17411874, -10912854, 9291594, -16247779, -12154742, 6048605 },
+ { -30305315, 14843444, 1539301, 11864366, 20201677, 1900163, 13934231, 5128323, 11213262, 9168384 },
+ },
+ {
+ { -26280513, 11007847, 19408960, -940758, -18592965, -4328580, -5088060, -11105150, 20470157, -16398701 },
+ { -23136053, 9282192, 14855179, -15390078, -7362815, -14408560, -22783952, 14461608, 14042978, 5230683 },
+ { 29969567, -2741594, -16711867, -8552442, 9175486, -2468974, 21556951, 3506042, -5933891, -12449708 },
+ },
+ {
+ { -3144746, 8744661, 19704003, 4581278, -20430686, 6830683, -21284170, 8971513, -28539189, 15326563 },
+ { -19464629, 10110288, -17262528, -3503892, -23500387, 1355669, -15523050, 15300988, -20514118, 9168260 },
+ { -5353335, 4488613, -23803248, 16314347, 7780487, -15638939, -28948358, 9601605, 33087103, -9011387 },
+ },
+ {
+ { -19443170, -15512900, -20797467, -12445323, -29824447, 10229461, -27444329, -15000531, -5996870, 15664672 },
+ { 23294591, -16632613, -22650781, -8470978, 27844204, 11461195, 13099750, -2460356, 18151676, 13417686 },
+ { -24722913, -4176517, -31150679, 5988919, -26858785, 6685065, 1661597, -12551441, 15271676, -15452665 },
+ },
+ },
+ {
+ {
+ { 11433042, -13228665, 8239631, -5279517, -1985436, -725718, -18698764, 2167544, -6921301, -13440182 },
+ { -31436171, 15575146, 30436815, 12192228, -22463353, 9395379, -9917708, -8638997, 12215110, 12028277 },
+ { 14098400, 6555944, 23007258, 5757252, -15427832, -12950502, 30123440, 4617780, -16900089, -655628 },
+ },
+ {
+ { -4026201, -15240835, 11893168, 13718664, -14809462, 1847385, -15819999, 10154009, 23973261, -12684474 },
+ { -26531820, -3695990, -1908898, 2534301, -31870557, -16550355, 18341390, -11419951, 32013174, -10103539 },
+ { -25479301, 10876443, -11771086, -14625140, -12369567, 1838104, 21911214, 6354752, 4425632, -837822 },
+ },
+ {
+ { -10433389, -14612966, 22229858, -3091047, -13191166, 776729, -17415375, -12020462, 4725005, 14044970 },
+ { 19268650, -7304421, 1555349, 8692754, -21474059, -9910664, 6347390, -1411784, -19522291, -16109756 },
+ { -24864089, 12986008, -10898878, -5558584, -11312371, -148526, 19541418, 8180106, 9282262, 10282508 },
+ },
+ {
+ { -26205082, 4428547, -8661196, -13194263, 4098402, -14165257, 15522535, 8372215, 5542595, -10702683 },
+ { -10562541, 14895633, 26814552, -16673850, -17480754, -2489360, -2781891, 6993761, -18093885, 10114655 },
+ { -20107055, -929418, 31422704, 10427861, -7110749, 6150669, -29091755, -11529146, 25953725, -106158 },
+ },
+ {
+ { -4234397, -8039292, -9119125, 3046000, 2101609, -12607294, 19390020, 6094296, -3315279, 12831125 },
+ { -15998678, 7578152, 5310217, 14408357, -33548620, -224739, 31575954, 6326196, 7381791, -2421839 },
+ { -20902779, 3296811, 24736065, -16328389, 18374254, 7318640, 6295303, 8082724, -15362489, 12339664 },
+ },
+ {
+ { 27724736, 2291157, 6088201, -14184798, 1792727, 5857634, 13848414, 15768922, 25091167, 14856294 },
+ { -18866652, 8331043, 24373479, 8541013, -701998, -9269457, 12927300, -12695493, -22182473, -9012899 },
+ { -11423429, -5421590, 11632845, 3405020, 30536730, -11674039, -27260765, 13866390, 30146206, 9142070 },
+ },
+ {
+ { 3924129, -15307516, -13817122, -10054960, 12291820, -668366, -27702774, 9326384, -8237858, 4171294 },
+ { -15921940, 16037937, 6713787, 16606682, -21612135, 2790944, 26396185, 3731949, 345228, -5462949 },
+ { -21327538, 13448259, 25284571, 1143661, 20614966, -8849387, 2031539, -12391231, -16253183, -13582083 },
+ },
+ {
+ { 31016211, -16722429, 26371392, -14451233, -5027349, 14854137, 17477601, 3842657, 28012650, -16405420 },
+ { -5075835, 9368966, -8562079, -4600902, -15249953, 6970560, -9189873, 16292057, -8867157, 3507940 },
+ { 29439664, 3537914, 23333589, 6997794, -17555561, -11018068, -15209202, -15051267, -9164929, 6580396 },
+ },
+ },
+ {
+ {
+ { -12185861, -7679788, 16438269, 10826160, -8696817, -6235611, 17860444, -9273846, -2095802, 9304567 },
+ { 20714564, -4336911, 29088195, 7406487, 11426967, -5095705, 14792667, -14608617, 5289421, -477127 },
+ { -16665533, -10650790, -6160345, -13305760, 9192020, -1802462, 17271490, 12349094, 26939669, -3752294 },
+ },
+ {
+ { -12889898, 9373458, 31595848, 16374215, 21471720, 13221525, -27283495, -12348559, -3698806, 117887 },
+ { 22263325, -6560050, 3984570, -11174646, -15114008, -566785, 28311253, 5358056, -23319780, 541964 },
+ { 16259219, 3261970, 2309254, -15534474, -16885711, -4581916, 24134070, -16705829, -13337066, -13552195 },
+ },
+ {
+ { 9378160, -13140186, -22845982, -12745264, 28198281, -7244098, -2399684, -717351, 690426, 14876244 },
+ { 24977353, -314384, -8223969, -13465086, 28432343, -1176353, -13068804, -12297348, -22380984, 6618999 },
+ { -1538174, 11685646, 12944378, 13682314, -24389511, -14413193, 8044829, -13817328, 32239829, -5652762 },
+ },
+ {
+ { -18603066, 4762990, -926250, 8885304, -28412480, -3187315, 9781647, -10350059, 32779359, 5095274 },
+ { -33008130, -5214506, -32264887, -3685216, 9460461, -9327423, -24601656, 14506724, 21639561, -2630236 },
+ { -16400943, -13112215, 25239338, 15531969, 3987758, -4499318, -1289502, -6863535, 17874574, 558605 },
+ },
+ {
+ { -13600129, 10240081, 9171883, 16131053, -20869254, 9599700, 33499487, 5080151, 2085892, 5119761 },
+ { -22205145, -2519528, -16381601, 414691, -25019550, 2170430, 30634760, -8363614, -31999993, -5759884 },
+ { -6845704, 15791202, 8550074, -1312654, 29928809, -12092256, 27534430, -7192145, -22351378, 12961482 },
+ },
+ {
+ { -24492060, -9570771, 10368194, 11582341, -23397293, -2245287, 16533930, 8206996, -30194652, -5159638 },
+ { -11121496, -3382234, 2307366, 6362031, -135455, 8868177, -16835630, 7031275, 7589640, 8945490 },
+ { -32152748, 8917967, 6661220, -11677616, -1192060, -15793393, 7251489, -11182180, 24099109, -14456170 },
+ },
+ {
+ { 5019558, -7907470, 4244127, -14714356, -26933272, 6453165, -19118182, -13289025, -6231896, -10280736 },
+ { 10853594, 10721687, 26480089, 5861829, -22995819, 1972175, -1866647, -10557898, -3363451, -6441124 },
+ { -17002408, 5906790, 221599, -6563147, 7828208, -13248918, 24362661, -2008168, -13866408, 7421392 },
+ },
+ {
+ { 8139927, -6546497, 32257646, -5890546, 30375719, 1886181, -21175108, 15441252, 28826358, -4123029 },
+ { 6267086, 9695052, 7709135, -16603597, -32869068, -1886135, 14795160, -7840124, 13746021, -1742048 },
+ { 28584902, 7787108, -6732942, -15050729, 22846041, -7571236, -3181936, -363524, 4771362, -8419958 },
+ },
+ },
+ {
+ {
+ { 24949256, 6376279, -27466481, -8174608, -18646154, -9930606, 33543569, -12141695, 3569627, 11342593 },
+ { 26514989, 4740088, 27912651, 3697550, 19331575, -11472339, 6809886, 4608608, 7325975, -14801071 },
+ { -11618399, -14554430, -24321212, 7655128, -1369274, 5214312, -27400540, 10258390, -17646694, -8186692 },
+ },
+ {
+ { 11431204, 15823007, 26570245, 14329124, 18029990, 4796082, -31446179, 15580664, 9280358, -3973687 },
+ { -160783, -10326257, -22855316, -4304997, -20861367, -13621002, -32810901, -11181622, -15545091, 4387441 },
+ { -20799378, 12194512, 3937617, -5805892, -27154820, 9340370, -24513992, 8548137, 20617071, -7482001 },
+ },
+ {
+ { -938825, -3930586, -8714311, 16124718, 24603125, -6225393, -13775352, -11875822, 24345683, 10325460 },
+ { -19855277, -1568885, -22202708, 8714034, 14007766, 6928528, 16318175, -1010689, 4766743, 3552007 },
+ { -21751364, -16730916, 1351763, -803421, -4009670, 3950935, 3217514, 14481909, 10988822, -3994762 },
+ },
+ {
+ { 15564307, -14311570, 3101243, 5684148, 30446780, -8051356, 12677127, -6505343, -8295852, 13296005 },
+ { -9442290, 6624296, -30298964, -11913677, -4670981, -2057379, 31521204, 9614054, -30000824, 12074674 },
+ { 4771191, -135239, 14290749, -13089852, 27992298, 14998318, -1413936, -1556716, 29832613, -16391035 },
+ },
+ {
+ { 7064884, -7541174, -19161962, -5067537, -18891269, -2912736, 25825242, 5293297, -27122660, 13101590 },
+ { -2298563, 2439670, -7466610, 1719965, -27267541, -16328445, 32512469, -5317593, -30356070, -4190957 },
+ { -30006540, 10162316, -33180176, 3981723, -16482138, -13070044, 14413974, 9515896, 19568978, 9628812 },
+ },
+ {
+ { 33053803, 199357, 15894591, 1583059, 27380243, -4580435, -17838894, -6106839, -6291786, 3437740 },
+ { -18978877, 3884493, 19469877, 12726490, 15913552, 13614290, -22961733, 70104, 7463304, 4176122 },
+ { -27124001, 10659917, 11482427, -16070381, 12771467, -6635117, -32719404, -5322751, 24216882, 5944158 },
+ },
+ {
+ { 8894125, 7450974, -2664149, -9765752, -28080517, -12389115, 19345746, 14680796, 11632993, 5847885 },
+ { 26942781, -2315317, 9129564, -4906607, 26024105, 11769399, -11518837, 6367194, -9727230, 4782140 },
+ { 19916461, -4828410, -22910704, -11414391, 25606324, -5972441, 33253853, 8220911, 6358847, -1873857 },
+ },
+ {
+ { 801428, -2081702, 16569428, 11065167, 29875704, 96627, 7908388, -4480480, -13538503, 1387155 },
+ { 19646058, 5720633, -11416706, 12814209, 11607948, 12749789, 14147075, 15156355, -21866831, 11835260 },
+ { 19299512, 1155910, 28703737, 14890794, 2925026, 7269399, 26121523, 15467869, -26560550, 5052483 },
+ },
+ },
+ {
+ {
+ { -3017432, 10058206, 1980837, 3964243, 22160966, 12322533, -6431123, -12618185, 12228557, -7003677 },
+ { 32944382, 14922211, -22844894, 5188528, 21913450, -8719943, 4001465, 13238564, -6114803, 8653815 },
+ { 22865569, -4652735, 27603668, -12545395, 14348958, 8234005, 24808405, 5719875, 28483275, 2841751 },
+ },
+ {
+ { -16420968, -1113305, -327719, -12107856, 21886282, -15552774, -1887966, -315658, 19932058, -12739203 },
+ { -11656086, 10087521, -8864888, -5536143, -19278573, -3055912, 3999228, 13239134, -4777469, -13910208 },
+ { 1382174, -11694719, 17266790, 9194690, -13324356, 9720081, 20403944, 11284705, -14013818, 3093230 },
+ },
+ {
+ { 16650921, -11037932, -1064178, 1570629, -8329746, 7352753, -302424, 16271225, -24049421, -6691850 },
+ { -21911077, -5927941, -4611316, -5560156, -31744103, -10785293, 24123614, 15193618, -21652117, -16739389 },
+ { -9935934, -4289447, -25279823, 4372842, 2087473, 10399484, 31870908, 14690798, 17361620, 11864968 },
+ },
+ {
+ { -11307610, 6210372, 13206574, 5806320, -29017692, -13967200, -12331205, -7486601, -25578460, -16240689 },
+ { 14668462, -12270235, 26039039, 15305210, 25515617, 4542480, 10453892, 6577524, 9145645, -6443880 },
+ { 5974874, 3053895, -9433049, -10385191, -31865124, 3225009, -7972642, 3936128, -5652273, -3050304 },
+ },
+ {
+ { 30625386, -4729400, -25555961, -12792866, -20484575, 7695099, 17097188, -16303496, -27999779, 1803632 },
+ { -3553091, 9865099, -5228566, 4272701, -5673832, -16689700, 14911344, 12196514, -21405489, 7047412 },
+ { 20093277, 9920966, -11138194, -5343857, 13161587, 12044805, -32856851, 4124601, -32343828, -10257566 },
+ },
+ {
+ { -20788824, 14084654, -13531713, 7842147, 19119038, -13822605, 4752377, -8714640, -21679658, 2288038 },
+ { -26819236, -3283715, 29965059, 3039786, -14473765, 2540457, 29457502, 14625692, -24819617, 12570232 },
+ { -1063558, -11551823, 16920318, 12494842, 1278292, -5869109, -21159943, -3498680, -11974704, 4724943 },
+ },
+ {
+ { 17960970, -11775534, -4140968, -9702530, -8876562, -1410617, -12907383, -8659932, -29576300, 1903856 },
+ { 23134274, -14279132, -10681997, -1611936, 20684485, 15770816, -12989750, 3190296, 26955097, 14109738 },
+ { 15308788, 5320727, -30113809, -14318877, 22902008, 7767164, 29425325, -11277562, 31960942, 11934971 },
+ },
+ {
+ { -27395711, 8435796, 4109644, 12222639, -24627868, 14818669, 20638173, 4875028, 10491392, 1379718 },
+ { -13159415, 9197841, 3875503, -8936108, -1383712, -5879801, 33518459, 16176658, 21432314, 12180697 },
+ { -11787308, 11500838, 13787581, -13832590, -22430679, 10140205, 1465425, 12689540, -10301319, -13872883 },
+ },
+ },
+ {
+ {
+ { 5414091, -15386041, -21007664, 9643570, 12834970, 1186149, -2622916, -1342231, 26128231, 6032912 },
+ { -26337395, -13766162, 32496025, -13653919, 17847801, -12669156, 3604025, 8316894, -25875034, -10437358 },
+ { 3296484, 6223048, 24680646, -12246460, -23052020, 5903205, -8862297, -4639164, 12376617, 3188849 },
+ },
+ {
+ { 29190488, -14659046, 27549113, -1183516, 3520066, -10697301, 32049515, -7309113, -16109234, -9852307 },
+ { -14744486, -9309156, 735818, -598978, -20407687, -5057904, 25246078, -15795669, 18640741, -960977 },
+ { -6928835, -16430795, 10361374, 5642961, 4910474, 12345252, -31638386, -494430, 10530747, 1053335 },
+ },
+ {
+ { -29265967, -14186805, -13538216, -12117373, -19457059, -10655384, -31462369, -2948985, 24018831, 15026644 },
+ { -22592535, -3145277, -2289276, 5953843, -13440189, 9425631, 25310643, 13003497, -2314791, -15145616 },
+ { -27419985, -603321, -8043984, -1669117, -26092265, 13987819, -27297622, 187899, -23166419, -2531735 },
+ },
+ {
+ { -21744398, -13810475, 1844840, 5021428, -10434399, -15911473, 9716667, 16266922, -5070217, 726099 },
+ { 29370922, -6053998, 7334071, -15342259, 9385287, 2247707, -13661962, -4839461, 30007388, -15823341 },
+ { -936379, 16086691, 23751945, -543318, -1167538, -5189036, 9137109, 730663, 9835848, 4555336 },
+ },
+ {
+ { -23376435, 1410446, -22253753, -12899614, 30867635, 15826977, 17693930, 544696, -11985298, 12422646 },
+ { 31117226, -12215734, -13502838, 6561947, -9876867, -12757670, -5118685, -4096706, 29120153, 13924425 },
+ { -17400879, -14233209, 19675799, -2734756, -11006962, -5858820, -9383939, -11317700, 7240931, -237388 },
+ },
+ {
+ { -31361739, -11346780, -15007447, -5856218, -22453340, -12152771, 1222336, 4389483, 3293637, -15551743 },
+ { -16684801, -14444245, 11038544, 11054958, -13801175, -3338533, -24319580, 7733547, 12796905, -6335822 },
+ { -8759414, -10817836, -25418864, 10783769, -30615557, -9746811, -28253339, 3647836, 3222231, -11160462 },
+ },
+ {
+ { 18606113, 1693100, -25448386, -15170272, 4112353, 10045021, 23603893, -2048234, -7550776, 2484985 },
+ { 9255317, -3131197, -12156162, -1004256, 13098013, -9214866, 16377220, -2102812, -19802075, -3034702 },
+ { -22729289, 7496160, -5742199, 11329249, 19991973, -3347502, -31718148, 9936966, -30097688, -10618797 },
+ },
+ {
+ { 21878590, -5001297, 4338336, 13643897, -3036865, 13160960, 19708896, 5415497, -7360503, -4109293 },
+ { 27736861, 10103576, 12500508, 8502413, -3413016, -9633558, 10436918, -1550276, -23659143, -8132100 },
+ { 19492550, -12104365, -29681976, -852630, -3208171, 12403437, 30066266, 8367329, 13243957, 8709688 },
+ },
+ },
+ {
+ {
+ { 12015105, 2801261, 28198131, 10151021, 24818120, -4743133, -11194191, -5645734, 5150968, 7274186 },
+ { 2831366, -12492146, 1478975, 6122054, 23825128, -12733586, 31097299, 6083058, 31021603, -9793610 },
+ { -2529932, -2229646, 445613, 10720828, -13849527, -11505937, -23507731, 16354465, 15067285, -14147707 },
+ },
+ {
+ { 7840942, 14037873, -33364863, 15934016, -728213, -3642706, 21403988, 1057586, -19379462, -12403220 },
+ { 915865, -16469274, 15608285, -8789130, -24357026, 6060030, -17371319, 8410997, -7220461, 16527025 },
+ { 32922597, -556987, 20336074, -16184568, 10903705, -5384487, 16957574, 52992, 23834301, 6588044 },
+ },
+ {
+ { 32752030, 11232950, 3381995, -8714866, 22652988, -10744103, 17159699, 16689107, -20314580, -1305992 },
+ { -4689649, 9166776, -25710296, -10847306, 11576752, 12733943, 7924251, -2752281, 1976123, -7249027 },
+ { 21251222, 16309901, -2983015, -6783122, 30810597, 12967303, 156041, -3371252, 12331345, -8237197 },
+ },
+ {
+ { 8651614, -4477032, -16085636, -4996994, 13002507, 2950805, 29054427, -5106970, 10008136, -4667901 },
+ { 31486080, 15114593, -14261250, 12951354, 14369431, -7387845, 16347321, -13662089, 8684155, -10532952 },
+ { 19443825, 11385320, 24468943, -9659068, -23919258, 2187569, -26263207, -6086921, 31316348, 14219878 },
+ },
+ {
+ { -28594490, 1193785, 32245219, 11392485, 31092169, 15722801, 27146014, 6992409, 29126555, 9207390 },
+ { 32382935, 1110093, 18477781, 11028262, -27411763, -7548111, -4980517, 10843782, -7957600, -14435730 },
+ { 2814918, 7836403, 27519878, -7868156, -20894015, -11553689, -21494559, 8550130, 28346258, 1994730 },
+ },
+ {
+ { -19578299, 8085545, -14000519, -3948622, 2785838, -16231307, -19516951, 7174894, 22628102, 8115180 },
+ { -30405132, 955511, -11133838, -15078069, -32447087, -13278079, -25651578, 3317160, -9943017, 930272 },
+ { -15303681, -6833769, 28856490, 1357446, 23421993, 1057177, 24091212, -1388970, -22765376, -10650715 },
+ },
+ {
+ { -22751231, -5303997, -12907607, -12768866, -15811511, -7797053, -14839018, -16554220, -1867018, 8398970 },
+ { -31969310, 2106403, -4736360, 1362501, 12813763, 16200670, 22981545, -6291273, 18009408, -15772772 },
+ { -17220923, -9545221, -27784654, 14166835, 29815394, 7444469, 29551787, -3727419, 19288549, 1325865 },
+ },
+ {
+ { 15100157, -15835752, -23923978, -1005098, -26450192, 15509408, 12376730, -3479146, 33166107, -8042750 },
+ { 20909231, 13023121, -9209752, 16251778, -5778415, -8094914, 12412151, 10018715, 2213263, -13878373 },
+ { 32529814, -11074689, 30361439, -16689753, -9135940, 1513226, 22922121, 6382134, -5766928, 8371348 },
+ },
+ },
+ {
+ {
+ { 9923462, 11271500, 12616794, 3544722, -29998368, -1721626, 12891687, -8193132, -26442943, 10486144 },
+ { -22597207, -7012665, 8587003, -8257861, 4084309, -12970062, 361726, 2610596, -23921530, -11455195 },
+ { 5408411, -1136691, -4969122, 10561668, 24145918, 14240566, 31319731, -4235541, 19985175, -3436086 },
+ },
+ {
+ { -13994457, 16616821, 14549246, 3341099, 32155958, 13648976, -17577068, 8849297, 65030, 8370684 },
+ { -8320926, -12049626, 31204563, 5839400, -20627288, -1057277, -19442942, 6922164, 12743482, -9800518 },
+ { -2361371, 12678785, 28815050, 4759974, -23893047, 4884717, 23783145, 11038569, 18800704, 255233 },
+ },
+ {
+ { -5269658, -1773886, 13957886, 7990715, 23132995, 728773, 13393847, 9066957, 19258688, -14753793 },
+ { -2936654, -10827535, -10432089, 14516793, -3640786, 4372541, -31934921, 2209390, -1524053, 2055794 },
+ { 580882, 16705327, 5468415, -2683018, -30926419, -14696000, -7203346, -8994389, -30021019, 7394435 },
+ },
+ {
+ { 23838809, 1822728, -15738443, 15242727, 8318092, -3733104, -21672180, -3492205, -4821741, 14799921 },
+ { 13345610, 9759151, 3371034, -16137791, 16353039, 8577942, 31129804, 13496856, -9056018, 7402518 },
+ { 2286874, -4435931, -20042458, -2008336, -13696227, 5038122, 11006906, -15760352, 8205061, 1607563 },
+ },
+ {
+ { 14414086, -8002132, 3331830, -3208217, 22249151, -5594188, 18364661, -2906958, 30019587, -9029278 },
+ { -27688051, 1585953, -10775053, 931069, -29120221, -11002319, -14410829, 12029093, 9944378, 8024 },
+ { 4368715, -3709630, 29874200, -15022983, -20230386, -11410704, -16114594, -999085, -8142388, 5640030 },
+ },
+ {
+ { 10299610, 13746483, 11661824, 16234854, 7630238, 5998374, 9809887, -16694564, 15219798, -14327783 },
+ { 27425505, -5719081, 3055006, 10660664, 23458024, 595578, -15398605, -1173195, -18342183, 9742717 },
+ { 6744077, 2427284, 26042789, 2720740, -847906, 1118974, 32324614, 7406442, 12420155, 1994844 },
+ },
+ {
+ { 14012521, -5024720, -18384453, -9578469, -26485342, -3936439, -13033478, -10909803, 24319929, -6446333 },
+ { 16412690, -4507367, 10772641, 15929391, -17068788, -4658621, 10555945, -10484049, -30102368, -4739048 },
+ { 22397382, -7767684, -9293161, -12792868, 17166287, -9755136, -27333065, 6199366, 21880021, -12250760 },
+ },
+ {
+ { -4283307, 5368523, -31117018, 8163389, -30323063, 3209128, 16557151, 8890729, 8840445, 4957760 },
+ { -15447727, 709327, -6919446, -10870178, -29777922, 6522332, -21720181, 12130072, -14796503, 5005757 },
+ { -2114751, -14308128, 23019042, 15765735, -25269683, 6002752, 10183197, -13239326, -16395286, -2176112 },
+ },
+ },
+ {
+ {
+ { -19025756, 1632005, 13466291, -7995100, -23640451, 16573537, -32013908, -3057104, 22208662, 2000468 },
+ { 3065073, -1412761, -25598674, -361432, -17683065, -5703415, -8164212, 11248527, -3691214, -7414184 },
+ { 10379208, -6045554, 8877319, 1473647, -29291284, -12507580, 16690915, 2553332, -3132688, 16400289 },
+ },
+ {
+ { 15716668, 1254266, -18472690, 7446274, -8448918, 6344164, -22097271, -7285580, 26894937, 9132066 },
+ { 24158887, 12938817, 11085297, -8177598, -28063478, -4457083, -30576463, 64452, -6817084, -2692882 },
+ { 13488534, 7794716, 22236231, 5989356, 25426474, -12578208, 2350710, -3418511, -4688006, 2364226 },
+ },
+ {
+ { 16335052, 9132434, 25640582, 6678888, 1725628, 8517937, -11807024, -11697457, 15445875, -7798101 },
+ { 29004207, -7867081, 28661402, -640412, -12794003, -7943086, 31863255, -4135540, -278050, -15759279 },
+ { -6122061, -14866665, -28614905, 14569919, -10857999, -3591829, 10343412, -6976290, -29828287, -10815811 },
+ },
+ {
+ { 27081650, 3463984, 14099042, -4517604, 1616303, -6205604, 29542636, 15372179, 17293797, 960709 },
+ { 20263915, 11434237, -5765435, 11236810, 13505955, -10857102, -16111345, 6493122, -19384511, 7639714 },
+ { -2830798, -14839232, 25403038, -8215196, -8317012, -16173699, 18006287, -16043750, 29994677, -15808121 },
+ },
+ {
+ { 9769828, 5202651, -24157398, -13631392, -28051003, -11561624, -24613141, -13860782, -31184575, 709464 },
+ { 12286395, 13076066, -21775189, -1176622, -25003198, 4057652, -32018128, -8890874, 16102007, 13205847 },
+ { 13733362, 5599946, 10557076, 3195751, -5557991, 8536970, -25540170, 8525972, 10151379, 10394400 },
+ },
+ {
+ { 4024660, -16137551, 22436262, 12276534, -9099015, -2686099, 19698229, 11743039, -33302334, 8934414 },
+ { -15879800, -4525240, -8580747, -2934061, 14634845, -698278, -9449077, 3137094, -11536886, 11721158 },
+ { 17555939, -5013938, 8268606, 2331751, -22738815, 9761013, 9319229, 8835153, -9205489, -1280045 },
+ },
+ {
+ { -461409, -7830014, 20614118, 16688288, -7514766, -4807119, 22300304, 505429, 6108462, -6183415 },
+ { -5070281, 12367917, -30663534, 3234473, 32617080, -8422642, 29880583, -13483331, -26898490, -7867459 },
+ { -31975283, 5726539, 26934134, 10237677, -3173717, -605053, 24199304, 3795095, 7592688, -14992079 },
+ },
+ {
+ { 21594432, -14964228, 17466408, -4077222, 32537084, 2739898, 6407723, 12018833, -28256052, 4298412 },
+ { -20650503, -11961496, -27236275, 570498, 3767144, -1717540, 13891942, -1569194, 13717174, 10805743 },
+ { -14676630, -15644296, 15287174, 11927123, 24177847, -8175568, -796431, 14860609, -26938930, -5863836 },
+ },
+ },
+ {
+ {
+ { 12962541, 5311799, -10060768, 11658280, 18855286, -7954201, 13286263, -12808704, -4381056, 9882022 },
+ { 18512079, 11319350, -20123124, 15090309, 18818594, 5271736, -22727904, 3666879, -23967430, -3299429 },
+ { -6789020, -3146043, 16192429, 13241070, 15898607, -14206114, -10084880, -6661110, -2403099, 5276065 },
+ },
+ {
+ { 30169808, -5317648, 26306206, -11750859, 27814964, 7069267, 7152851, 3684982, 1449224, 13082861 },
+ { 10342826, 3098505, 2119311, 193222, 25702612, 12233820, 23697382, 15056736, -21016438, -8202000 },
+ { -33150110, 3261608, 22745853, 7948688, 19370557, -15177665, -26171976, 6482814, -10300080, -11060101 },
+ },
+ {
+ { 32869458, -5408545, 25609743, 15678670, -10687769, -15471071, 26112421, 2521008, -22664288, 6904815 },
+ { 29506923, 4457497, 3377935, -9796444, -30510046, 12935080, 1561737, 3841096, -29003639, -6657642 },
+ { 10340844, -6630377, -18656632, -2278430, 12621151, -13339055, 30878497, -11824370, -25584551, 5181966 },
+ },
+ {
+ { 25940115, -12658025, 17324188, -10307374, -8671468, 15029094, 24396252, -16450922, -2322852, -12388574 },
+ { -21765684, 9916823, -1300409, 4079498, -1028346, 11909559, 1782390, 12641087, 20603771, -6561742 },
+ { -18882287, -11673380, 24849422, 11501709, 13161720, -4768874, 1925523, 11914390, 4662781, 7820689 },
+ },
+ {
+ { 12241050, -425982, 8132691, 9393934, 32846760, -1599620, 29749456, 12172924, 16136752, 15264020 },
+ { -10349955, -14680563, -8211979, 2330220, -17662549, -14545780, 10658213, 6671822, 19012087, 3772772 },
+ { 3753511, -3421066, 10617074, 2028709, 14841030, -6721664, 28718732, -15762884, 20527771, 12988982 },
+ },
+ {
+ { -14822485, -5797269, -3707987, 12689773, -898983, -10914866, -24183046, -10564943, 3299665, -12424953 },
+ { -16777703, -15253301, -9642417, 4978983, 3308785, 8755439, 6943197, 6461331, -25583147, 8991218 },
+ { -17226263, 1816362, -1673288, -6086439, 31783888, -8175991, -32948145, 7417950, -30242287, 1507265 },
+ },
+ {
+ { 29692663, 6829891, -10498800, 4334896, 20945975, -11906496, -28887608, 8209391, 14606362, -10647073 },
+ { -3481570, 8707081, 32188102, 5672294, 22096700, 1711240, -33020695, 9761487, 4170404, -2085325 },
+ { -11587470, 14855945, -4127778, -1531857, -26649089, 15084046, 22186522, 16002000, -14276837, -8400798 },
+ },
+ {
+ { -4811456, 13761029, -31703877, -2483919, -3312471, 7869047, -7113572, -9620092, 13240845, 10965870 },
+ { -7742563, -8256762, -14768334, -13656260, -23232383, 12387166, 4498947, 14147411, 29514390, 4302863 },
+ { -13413405, -12407859, 20757302, -13801832, 14785143, 8976368, -5061276, -2144373, 17846988, -13971927 },
+ },
+ },
+ {
+ {
+ { -2244452, -754728, -4597030, -1066309, -6247172, 1455299, -21647728, -9214789, -5222701, 12650267 },
+ { -9906797, -16070310, 21134160, 12198166, -27064575, 708126, 387813, 13770293, -19134326, 10958663 },
+ { 22470984, 12369526, 23446014, -5441109, -21520802, -9698723, -11772496, -11574455, -25083830, 4271862 },
+ },
+ {
+ { -25169565, -10053642, -19909332, 15361595, -5984358, 2159192, 75375, -4278529, -32526221, 8469673 },
+ { 15854970, 4148314, -8893890, 7259002, 11666551, 13824734, -30531198, 2697372, 24154791, -9460943 },
+ { 15446137, -15806644, 29759747, 14019369, 30811221, -9610191, -31582008, 12840104, 24913809, 9815020 },
+ },
+ {
+ { -4709286, -5614269, -31841498, -12288893, -14443537, 10799414, -9103676, 13438769, 18735128, 9466238 },
+ { 11933045, 9281483, 5081055, -5183824, -2628162, -4905629, -7727821, -10896103, -22728655, 16199064 },
+ { 14576810, 379472, -26786533, -8317236, -29426508, -10812974, -102766, 1876699, 30801119, 2164795 },
+ },
+ {
+ { 15995086, 3199873, 13672555, 13712240, -19378835, -4647646, -13081610, -15496269, -13492807, 1268052 },
+ { -10290614, -3659039, -3286592, 10948818, 23037027, 3794475, -3470338, -12600221, -17055369, 3565904 },
+ { 29210088, -9419337, -5919792, -4952785, 10834811, -13327726, -16512102, -10820713, -27162222, -14030531 },
+ },
+ {
+ { -13161890, 15508588, 16663704, -8156150, -28349942, 9019123, -29183421, -3769423, 2244111, -14001979 },
+ { -5152875, -3800936, -9306475, -6071583, 16243069, 14684434, -25673088, -16180800, 13491506, 4641841 },
+ { 10813417, 643330, -19188515, -728916, 30292062, -16600078, 27548447, -7721242, 14476989, -12767431 },
+ },
+ {
+ { 10292079, 9984945, 6481436, 8279905, -7251514, 7032743, 27282937, -1644259, -27912810, 12651324 },
+ { -31185513, -813383, 22271204, 11835308, 10201545, 15351028, 17099662, 3988035, 21721536, -3148940 },
+ { 10202177, -6545839, -31373232, -9574638, -32150642, -8119683, -12906320, 3852694, 13216206, 14842320 },
+ },
+ {
+ { -15815640, -10601066, -6538952, -7258995, -6984659, -6581778, -31500847, 13765824, -27434397, 9900184 },
+ { 14465505, -13833331, -32133984, -14738873, -27443187, 12990492, 33046193, 15796406, -7051866, -8040114 },
+ { 30924417, -8279620, 6359016, -12816335, 16508377, 9071735, -25488601, 15413635, 9524356, -7018878 },
+ },
+ {
+ { 12274201, -13175547, 32627641, -1785326, 6736625, 13267305, 5237659, -5109483, 15663516, 4035784 },
+ { -2951309, 8903985, 17349946, 601635, -16432815, -4612556, -13732739, -15889334, -22258478, 4659091 },
+ { -16916263, -4952973, -30393711, -15158821, 20774812, 15897498, 5736189, 15026997, -2178256, -13455585 },
+ },
+ },
+ {
+ {
+ { -8858980, -2219056, 28571666, -10155518, -474467, -10105698, -3801496, 278095, 23440562, -290208 },
+ { 10226241, -5928702, 15139956, 120818, -14867693, 5218603, 32937275, 11551483, -16571960, -7442864 },
+ { 17932739, -12437276, -24039557, 10749060, 11316803, 7535897, 22503767, 5561594, -3646624, 3898661 },
+ },
+ {
+ { 7749907, -969567, -16339731, -16464, -25018111, 15122143, -1573531, 7152530, 21831162, 1245233 },
+ { 26958459, -14658026, 4314586, 8346991, -5677764, 11960072, -32589295, -620035, -30402091, -16716212 },
+ { -12165896, 9166947, 33491384, 13673479, 29787085, 13096535, 6280834, 14587357, -22338025, 13987525 },
+ },
+ {
+ { -24349909, 7778775, 21116000, 15572597, -4833266, -5357778, -4300898, -5124639, -7469781, -2858068 },
+ { 9681908, -6737123, -31951644, 13591838, -6883821, 386950, 31622781, 6439245, -14581012, 4091397 },
+ { -8426427, 1470727, -28109679, -1596990, 3978627, -5123623, -19622683, 12092163, 29077877, -14741988 },
+ },
+ {
+ { 5269168, -6859726, -13230211, -8020715, 25932563, 1763552, -5606110, -5505881, -20017847, 2357889 },
+ { 32264008, -15407652, -5387735, -1160093, -2091322, -3946900, 23104804, -12869908, 5727338, 189038 },
+ { 14609123, -8954470, -6000566, -16622781, -14577387, -7743898, -26745169, 10942115, -25888931, -14884697 },
+ },
+ {
+ { 20513500, 5557931, -15604613, 7829531, 26413943, -2019404, -21378968, 7471781, 13913677, -5137875 },
+ { -25574376, 11967826, 29233242, 12948236, -6754465, 4713227, -8940970, 14059180, 12878652, 8511905 },
+ { -25656801, 3393631, -2955415, -7075526, -2250709, 9366908, -30223418, 6812974, 5568676, -3127656 },
+ },
+ {
+ { 11630004, 12144454, 2116339, 13606037, 27378885, 15676917, -17408753, -13504373, -14395196, 8070818 },
+ { 27117696, -10007378, -31282771, -5570088, 1127282, 12772488, -29845906, 10483306, -11552749, -1028714 },
+ { 10637467, -5688064, 5674781, 1072708, -26343588, -6982302, -1683975, 9177853, -27493162, 15431203 },
+ },
+ {
+ { 20525145, 10892566, -12742472, 12779443, -29493034, 16150075, -28240519, 14943142, -15056790, -7935931 },
+ { -30024462, 5626926, -551567, -9981087, 753598, 11981191, 25244767, -3239766, -3356550, 9594024 },
+ { -23752644, 2636870, -5163910, -10103818, 585134, 7877383, 11345683, -6492290, 13352335, -10977084 },
+ },
+ {
+ { -1931799, -5407458, 3304649, -12884869, 17015806, -4877091, -29783850, -7752482, -13215537, -319204 },
+ { 20239939, 6607058, 6203985, 3483793, -18386976, -779229, -20723742, 15077870, -22750759, 14523817 },
+ { 27406042, -6041657, 27423596, -4497394, 4996214, 10002360, -28842031, -4545494, -30172742, -4805667 },
+ },
+ },
+ {
+ {
+ { 11374242, 12660715, 17861383, -12540833, 10935568, 1099227, -13886076, -9091740, -27727044, 11358504 },
+ { -12730809, 10311867, 1510375, 10778093, -2119455, -9145702, 32676003, 11149336, -26123651, 4985768 },
+ { -19096303, 341147, -6197485, -239033, 15756973, -8796662, -983043, 13794114, -19414307, -15621255 },
+ },
+ {
+ { 6490081, 11940286, 25495923, -7726360, 8668373, -8751316, 3367603, 6970005, -1691065, -9004790 },
+ { 1656497, 13457317, 15370807, 6364910, 13605745, 8362338, -19174622, -5475723, -16796596, -5031438 },
+ { -22273315, -13524424, -64685, -4334223, -18605636, -10921968, -20571065, -7007978, -99853, -10237333 },
+ },
+ {
+ { 17747465, 10039260, 19368299, -4050591, -20630635, -16041286, 31992683, -15857976, -29260363, -5511971 },
+ { 31932027, -4986141, -19612382, 16366580, 22023614, 88450, 11371999, -3744247, 4882242, -10626905 },
+ { 29796507, 37186, 19818052, 10115756, -11829032, 3352736, 18551198, 3272828, -5190932, -4162409 },
+ },
+ {
+ { 12501286, 4044383, -8612957, -13392385, -32430052, 5136599, -19230378, -3529697, 330070, -3659409 },
+ { 6384877, 2899513, 17807477, 7663917, -2358888, 12363165, 25366522, -8573892, -271295, 12071499 },
+ { -8365515, -4042521, 25133448, -4517355, -6211027, 2265927, -32769618, 1936675, -5159697, 3829363 },
+ },
+ {
+ { 28425966, -5835433, -577090, -4697198, -14217555, 6870930, 7921550, -6567787, 26333140, 14267664 },
+ { -11067219, 11871231, 27385719, -10559544, -4585914, -11189312, 10004786, -8709488, -21761224, 8930324 },
+ { -21197785, -16396035, 25654216, -1725397, 12282012, 11008919, 1541940, 4757911, -26491501, -16408940 },
+ },
+ {
+ { 13537262, -7759490, -20604840, 10961927, -5922820, -13218065, -13156584, 6217254, -15943699, 13814990 },
+ { -17422573, 15157790, 18705543, 29619, 24409717, -260476, 27361681, 9257833, -1956526, -1776914 },
+ { -25045300, -10191966, 15366585, 15166509, -13105086, 8423556, -29171540, 12361135, -18685978, 4578290 },
+ },
+ {
+ { 24579768, 3711570, 1342322, -11180126, -27005135, 14124956, -22544529, 14074919, 21964432, 8235257 },
+ { -6528613, -2411497, 9442966, -5925588, 12025640, -1487420, -2981514, -1669206, 13006806, 2355433 },
+ { -16304899, -13605259, -6632427, -5142349, 16974359, -10911083, 27202044, 1719366, 1141648, -12796236 },
+ },
+ {
+ { -12863944, -13219986, -8318266, -11018091, -6810145, -4843894, 13475066, -3133972, 32674895, 13715045 },
+ { 11423335, -5468059, 32344216, 8962751, 24989809, 9241752, -13265253, 16086212, -28740881, -15642093 },
+ { -1409668, 12530728, -6368726, 10847387, 19531186, -14132160, -11709148, 7791794, -27245943, 4383347 },
+ },
+ },
+ {
+ {
+ { -28970898, 5271447, -1266009, -9736989, -12455236, 16732599, -4862407, -4906449, 27193557, 6245191 },
+ { -15193956, 5362278, -1783893, 2695834, 4960227, 12840725, 23061898, 3260492, 22510453, 8577507 },
+ { -12632451, 11257346, -32692994, 13548177, -721004, 10879011, 31168030, 13952092, -29571492, -3635906 },
+ },
+ {
+ { 3877321, -9572739, 32416692, 5405324, -11004407, -13656635, 3759769, 11935320, 5611860, 8164018 },
+ { -16275802, 14667797, 15906460, 12155291, -22111149, -9039718, 32003002, -8832289, 5773085, -8422109 },
+ { -23788118, -8254300, 1950875, 8937633, 18686727, 16459170, -905725, 12376320, 31632953, 190926 },
+ },
+ {
+ { -24593607, -16138885, -8423991, 13378746, 14162407, 6901328, -8288749, 4508564, -25341555, -3627528 },
+ { 8884438, -5884009, 6023974, 10104341, -6881569, -4941533, 18722941, -14786005, -1672488, 827625 },
+ { -32720583, -16289296, -32503547, 7101210, 13354605, 2659080, -1800575, -14108036, -24878478, 1541286 },
+ },
+ {
+ { 2901347, -1117687, 3880376, -10059388, -17620940, -3612781, -21802117, -3567481, 20456845, -1885033 },
+ { 27019610, 12299467, -13658288, -1603234, -12861660, -4861471, -19540150, -5016058, 29439641, 15138866 },
+ { 21536104, -6626420, -32447818, -10690208, -22408077, 5175814, -5420040, -16361163, 7779328, 109896 },
+ },
+ {
+ { 30279744, 14648750, -8044871, 6425558, 13639621, -743509, 28698390, 12180118, 23177719, -554075 },
+ { 26572847, 3405927, -31701700, 12890905, -19265668, 5335866, -6493768, 2378492, 4439158, -13279347 },
+ { -22716706, 3489070, -9225266, -332753, 18875722, -1140095, 14819434, -12731527, -17717757, -5461437 },
+ },
+ {
+ { -5056483, 16566551, 15953661, 3767752, -10436499, 15627060, -820954, 2177225, 8550082, -15114165 },
+ { -18473302, 16596775, -381660, 15663611, 22860960, 15585581, -27844109, -3582739, -23260460, -8428588 },
+ { -32480551, 15707275, -8205912, -5652081, 29464558, 2713815, -22725137, 15860482, -21902570, 1494193 },
+ },
+ {
+ { -19562091, -14087393, -25583872, -9299552, 13127842, 759709, 21923482, 16529112, 8742704, 12967017 },
+ { -28464899, 1553205, 32536856, -10473729, -24691605, -406174, -8914625, -2933896, -29903758, 15553883 },
+ { 21877909, 3230008, 9881174, 10539357, -4797115, 2841332, 11543572, 14513274, 19375923, -12647961 },
+ },
+ {
+ { 8832269, -14495485, 13253511, 5137575, 5037871, 4078777, 24880818, -6222716, 2862653, 9455043 },
+ { 29306751, 5123106, 20245049, -14149889, 9592566, 8447059, -2077124, -2990080, 15511449, 4789663 },
+ { -20679756, 7004547, 8824831, -9434977, -4045704, -3750736, -5754762, 108893, 23513200, 16652362 },
+ },
+ },
+ {
+ {
+ { -33256173, 4144782, -4476029, -6579123, 10770039, -7155542, -6650416, -12936300, -18319198, 10212860 },
+ { 2756081, 8598110, 7383731, -6859892, 22312759, -1105012, 21179801, 2600940, -9988298, -12506466 },
+ { -24645692, 13317462, -30449259, -15653928, 21365574, -10869657, 11344424, 864440, -2499677, -16710063 },
+ },
+ {
+ { -26432803, 6148329, -17184412, -14474154, 18782929, -275997, -22561534, 211300, 2719757, 4940997 },
+ { -1323882, 3911313, -6948744, 14759765, -30027150, 7851207, 21690126, 8518463, 26699843, 5276295 },
+ { -13149873, -6429067, 9396249, 365013, 24703301, -10488939, 1321586, 149635, -15452774, 7159369 },
+ },
+ {
+ { 9987780, -3404759, 17507962, 9505530, 9731535, -2165514, 22356009, 8312176, 22477218, -8403385 },
+ { 18155857, -16504990, 19744716, 9006923, 15154154, -10538976, 24256460, -4864995, -22548173, 9334109 },
+ { 2986088, -4911893, 10776628, -3473844, 10620590, -7083203, -21413845, 14253545, -22587149, 536906 },
+ },
+ {
+ { 4377756, 8115836, 24567078, 15495314, 11625074, 13064599, 7390551, 10589625, 10838060, -15420424 },
+ { -19342404, 867880, 9277171, -3218459, -14431572, -1986443, 19295826, -15796950, 6378260, 699185 },
+ { 7895026, 4057113, -7081772, -13077756, -17886831, -323126, -716039, 15693155, -5045064, -13373962 },
+ },
+ {
+ { -7737563, -5869402, -14566319, -7406919, 11385654, 13201616, 31730678, -10962840, -3918636, -9669325 },
+ { 10188286, -15770834, -7336361, 13427543, 22223443, 14896287, 30743455, 7116568, -21786507, 5427593 },
+ { 696102, 13206899, 27047647, -10632082, 15285305, -9853179, 10798490, -4578720, 19236243, 12477404 },
+ },
+ {
+ { -11229439, 11243796, -17054270, -8040865, -788228, -8167967, -3897669, 11180504, -23169516, 7733644 },
+ { 17800790, -14036179, -27000429, -11766671, 23887827, 3149671, 23466177, -10538171, 10322027, 15313801 },
+ { 26246234, 11968874, 32263343, -5468728, 6830755, -13323031, -15794704, -101982, -24449242, 10890804 },
+ },
+ {
+ { -31365647, 10271363, -12660625, -6267268, 16690207, -13062544, -14982212, 16484931, 25180797, -5334884 },
+ { -586574, 10376444, -32586414, -11286356, 19801893, 10997610, 2276632, 9482883, 316878, 13820577 },
+ { -9882808, -4510367, -2115506, 16457136, -11100081, 11674996, 30756178, -7515054, 30696930, -3712849 },
+ },
+ {
+ { 32988917, -9603412, 12499366, 7910787, -10617257, -11931514, -7342816, -9985397, -32349517, 7392473 },
+ { -8855661, 15927861, 9866406, -3649411, -2396914, -16655781, -30409476, -9134995, 25112947, -2926644 },
+ { -2504044, -436966, 25621774, -5678772, 15085042, -5479877, -24884878, -13526194, 5537438, -13914319 },
+ },
+ },
+ {
+ {
+ { -11225584, 2320285, -9584280, 10149187, -33444663, 5808648, -14876251, -1729667, 31234590, 6090599 },
+ { -9633316, 116426, 26083934, 2897444, -6364437, -2688086, 609721, 15878753, -6970405, -9034768 },
+ { -27757857, 247744, -15194774, -9002551, 23288161, -10011936, -23869595, 6503646, 20650474, 1804084 },
+ },
+ {
+ { -27589786, 15456424, 8972517, 8469608, 15640622, 4439847, 3121995, -10329713, 27842616, -202328 },
+ { -15306973, 2839644, 22530074, 10026331, 4602058, 5048462, 28248656, 5031932, -11375082, 12714369 },
+ { 20807691, -7270825, 29286141, 11421711, -27876523, -13868230, -21227475, 1035546, -19733229, 12796920 },
+ },
+ {
+ { 12076899, -14301286, -8785001, -11848922, -25012791, 16400684, -17591495, -12899438, 3480665, -15182815 },
+ { -32361549, 5457597, 28548107, 7833186, 7303070, -11953545, -24363064, -15921875, -33374054, 2771025 },
+ { -21389266, 421932, 26597266, 6860826, 22486084, -6737172, -17137485, -4210226, -24552282, 15673397 },
+ },
+ {
+ { -20184622, 2338216, 19788685, -9620956, -4001265, -8740893, -20271184, 4733254, 3727144, -12934448 },
+ { 6120119, 814863, -11794402, -622716, 6812205, -15747771, 2019594, 7975683, 31123697, -10958981 },
+ { 30069250, -11435332, 30434654, 2958439, 18399564, -976289, 12296869, 9204260, -16432438, 9648165 },
+ },
+ {
+ { 32705432, -1550977, 30705658, 7451065, -11805606, 9631813, 3305266, 5248604, -26008332, -11377501 },
+ { 17219865, 2375039, -31570947, -5575615, -19459679, 9219903, 294711, 15298639, 2662509, -16297073 },
+ { -1172927, -7558695, -4366770, -4287744, -21346413, -8434326, 32087529, -1222777, 32247248, -14389861 },
+ },
+ {
+ { 14312628, 1221556, 17395390, -8700143, -4945741, -8684635, -28197744, -9637817, -16027623, -13378845 },
+ { -1428825, -9678990, -9235681, 6549687, -7383069, -468664, 23046502, 9803137, 17597934, 2346211 },
+ { 18510800, 15337574, 26171504, 981392, -22241552, 7827556, -23491134, -11323352, 3059833, -11782870 },
+ },
+ {
+ { 10141598, 6082907, 17829293, -1947643, 9830092, 13613136, -25556636, -5544586, -33502212, 3592096 },
+ { 33114168, -15889352, -26525686, -13343397, 33076705, 8716171, 1151462, 1521897, -982665, -6837803 },
+ { -32939165, -4255815, 23947181, -324178, -33072974, -12305637, -16637686, 3891704, 26353178, 693168 },
+ },
+ {
+ { 30374239, 1595580, -16884039, 13186931, 4600344, 406904, 9585294, -400668, 31375464, 14369965 },
+ { -14370654, -7772529, 1510301, 6434173, -18784789, -6262728, 32732230, -13108839, 17901441, 16011505 },
+ { 18171223, -11934626, -12500402, 15197122, -11038147, -15230035, -19172240, -16046376, 8764035, 12309598 },
+ },
+ },
+ {
+ {
+ { 5975908, -5243188, -19459362, -9681747, -11541277, 14015782, -23665757, 1228319, 17544096, -10593782 },
+ { 5811932, -1715293, 3442887, -2269310, -18367348, -8359541, -18044043, -15410127, -5565381, 12348900 },
+ { -31399660, 11407555, 25755363, 6891399, -3256938, 14872274, -24849353, 8141295, -10632534, -585479 },
+ },
+ {
+ { -12675304, 694026, -5076145, 13300344, 14015258, -14451394, -9698672, -11329050, 30944593, 1130208 },
+ { 8247766, -6710942, -26562381, -7709309, -14401939, -14648910, 4652152, 2488540, 23550156, -271232 },
+ { 17294316, -3788438, 7026748, 15626851, 22990044, 113481, 2267737, -5908146, -408818, -137719 },
+ },
+ {
+ { 16091085, -16253926, 18599252, 7340678, 2137637, -1221657, -3364161, 14550936, 3260525, -7166271 },
+ { -4910104, -13332887, 18550887, 10864893, -16459325, -7291596, -23028869, -13204905, -12748722, 2701326 },
+ { -8574695, 16099415, 4629974, -16340524, -20786213, -6005432, -10018363, 9276971, 11329923, 1862132 },
+ },
+ {
+ { 14763076, -15903608, -30918270, 3689867, 3511892, 10313526, -21951088, 12219231, -9037963, -940300 },
+ { 8894987, -3446094, 6150753, 3013931, 301220, 15693451, -31981216, -2909717, -15438168, 11595570 },
+ { 15214962, 3537601, -26238722, -14058872, 4418657, -15230761, 13947276, 10730794, -13489462, -4363670 },
+ },
+ {
+ { -2538306, 7682793, 32759013, 263109, -29984731, -7955452, -22332124, -10188635, 977108, 699994 },
+ { -12466472, 4195084, -9211532, 550904, -15565337, 12917920, 19118110, -439841, -30534533, -14337913 },
+ { 31788461, -14507657, 4799989, 7372237, 8808585, -14747943, 9408237, -10051775, 12493932, -5409317 },
+ },
+ {
+ { -25680606, 5260744, -19235809, -6284470, -3695942, 16566087, 27218280, 2607121, 29375955, 6024730 },
+ { 842132, -2794693, -4763381, -8722815, 26332018, -12405641, 11831880, 6985184, -9940361, 2854096 },
+ { -4847262, -7969331, 2516242, -5847713, 9695691, -7221186, 16512645, 960770, 12121869, 16648078 },
+ },
+ {
+ { -15218652, 14667096, -13336229, 2013717, 30598287, -464137, -31504922, -7882064, 20237806, 2838411 },
+ { -19288047, 4453152, 15298546, -16178388, 22115043, -15972604, 12544294, -13470457, 1068881, -12499905 },
+ { -9558883, -16518835, 33238498, 13506958, 30505848, -1114596, -8486907, -2630053, 12521378, 4845654 },
+ },
+ {
+ { -28198521, 10744108, -2958380, 10199664, 7759311, -13088600, 3409348, -873400, -6482306, -12885870 },
+ { -23561822, 6230156, -20382013, 10655314, -24040585, -11621172, 10477734, -1240216, -3113227, 13974498 },
+ { 12966261, 15550616, -32038948, -1615346, 21025980, -629444, 5642325, 7188737, 18895762, 12629579 },
+ },
+ },
+ {
+ {
+ { 14741879, -14946887, 22177208, -11721237, 1279741, 8058600, 11758140, 789443, 32195181, 3895677 },
+ { 10758205, 15755439, -4509950, 9243698, -4879422, 6879879, -2204575, -3566119, -8982069, 4429647 },
+ { -2453894, 15725973, -20436342, -10410672, -5803908, -11040220, -7135870, -11642895, 18047436, -15281743 },
+ },
+ {
+ { -25173001, -11307165, 29759956, 11776784, -22262383, -15820455, 10993114, -12850837, -17620701, -9408468 },
+ { 21987233, 700364, -24505048, 14972008, -7774265, -5718395, 32155026, 2581431, -29958985, 8773375 },
+ { -25568350, 454463, -13211935, 16126715, 25240068, 8594567, 20656846, 12017935, -7874389, -13920155 },
+ },
+ {
+ { 6028182, 6263078, -31011806, -11301710, -818919, 2461772, -31841174, -5468042, -1721788, -2776725 },
+ { -12278994, 16624277, 987579, -5922598, 32908203, 1248608, 7719845, -4166698, 28408820, 6816612 },
+ { -10358094, -8237829, 19549651, -12169222, 22082623, 16147817, 20613181, 13982702, -10339570, 5067943 },
+ },
+ {
+ { -30505967, -3821767, 12074681, 13582412, -19877972, 2443951, -19719286, 12746132, 5331210, -10105944 },
+ { 30528811, 3601899, -1957090, 4619785, -27361822, -15436388, 24180793, -12570394, 27679908, -1648928 },
+ { 9402404, -13957065, 32834043, 10838634, -26580150, -13237195, 26653274, -8685565, 22611444, -12715406 },
+ },
+ {
+ { 22190590, 1118029, 22736441, 15130463, -30460692, -5991321, 19189625, -4648942, 4854859, 6622139 },
+ { -8310738, -2953450, -8262579, -3388049, -10401731, -271929, 13424426, -3567227, 26404409, 13001963 },
+ { -31241838, -15415700, -2994250, 8939346, 11562230, -12840670, -26064365, -11621720, -15405155, 11020693 },
+ },
+ {
+ { 1866042, -7949489, -7898649, -10301010, 12483315, 13477547, 3175636, -12424163, 28761762, 1406734 },
+ { -448555, -1777666, 13018551, 3194501, -9580420, -11161737, 24760585, -4347088, 25577411, -13378680 },
+ { -24290378, 4759345, -690653, -1852816, 2066747, 10693769, -29595790, 9884936, -9368926, 4745410 },
+ },
+ {
+ { -9141284, 6049714, -19531061, -4341411, -31260798, 9944276, -15462008, -11311852, 10931924, -11931931 },
+ { -16561513, 14112680, -8012645, 4817318, -8040464, -11414606, -22853429, 10856641, -20470770, 13434654 },
+ { 22759489, -10073434, -16766264, -1871422, 13637442, -10168091, 1765144, -12654326, 28445307, -5364710 },
+ },
+ {
+ { 29875063, 12493613, 2795536, -3786330, 1710620, 15181182, -10195717, -8788675, 9074234, 1167180 },
+ { -26205683, 11014233, -9842651, -2635485, -26908120, 7532294, -18716888, -9535498, 3843903, 9367684 },
+ { -10969595, -6403711, 9591134, 9582310, 11349256, 108879, 16235123, 8601684, -139197, 4242895 },
+ },
+ },
+ {
+ {
+ { 22092954, -13191123, -2042793, -11968512, 32186753, -11517388, -6574341, 2470660, -27417366, 16625501 },
+ { -11057722, 3042016, 13770083, -9257922, 584236, -544855, -7770857, 2602725, -27351616, 14247413 },
+ { 6314175, -10264892, -32772502, 15957557, -10157730, 168750, -8618807, 14290061, 27108877, -1180880 },
+ },
+ {
+ { -8586597, -7170966, 13241782, 10960156, -32991015, -13794596, 33547976, -11058889, -27148451, 981874 },
+ { 22833440, 9293594, -32649448, -13618667, -9136966, 14756819, -22928859, -13970780, -10479804, -16197962 },
+ { -7768587, 3326786, -28111797, 10783824, 19178761, 14905060, 22680049, 13906969, -15933690, 3797899 },
+ },
+ {
+ { 21721356, -4212746, -12206123, 9310182, -3882239, -13653110, 23740224, -2709232, 20491983, -8042152 },
+ { 9209270, -15135055, -13256557, -6167798, -731016, 15289673, 25947805, 15286587, 30997318, -6703063 },
+ { 7392032, 16618386, 23946583, -8039892, -13265164, -1533858, -14197445, -2321576, 17649998, -250080 },
+ },
+ {
+ { -9301088, -14193827, 30609526, -3049543, -25175069, -1283752, -15241566, -9525724, -2233253, 7662146 },
+ { -17558673, 1763594, -33114336, 15908610, -30040870, -12174295, 7335080, -8472199, -3174674, 3440183 },
+ { -19889700, -5977008, -24111293, -9688870, 10799743, -16571957, 40450, -4431835, 4862400, 1133 },
+ },
+ {
+ { -32856209, -7873957, -5422389, 14860950, -16319031, 7956142, 7258061, 311861, -30594991, -7379421 },
+ { -3773428, -1565936, 28985340, 7499440, 24445838, 9325937, 29727763, 16527196, 18278453, 15405622 },
+ { -4381906, 8508652, -19898366, -3674424, -5984453, 15149970, -13313598, 843523, -21875062, 13626197 },
+ },
+ {
+ { 2281448, -13487055, -10915418, -2609910, 1879358, 16164207, -10783882, 3953792, 13340839, 15928663 },
+ { 31727126, -7179855, -18437503, -8283652, 2875793, -16390330, -25269894, -7014826, -23452306, 5964753 },
+ { 4100420, -5959452, -17179337, 6017714, -18705837, 12227141, -26684835, 11344144, 2538215, -7570755 },
+ },
+ {
+ { -9433605, 6123113, 11159803, -2156608, 30016280, 14966241, -20474983, 1485421, -629256, -15958862 },
+ { -26804558, 4260919, 11851389, 9658551, -32017107, 16367492, -20205425, -13191288, 11659922, -11115118 },
+ { 26180396, 10015009, -30844224, -8581293, 5418197, 9480663, 2231568, -10170080, 33100372, -1306171 },
+ },
+ {
+ { 15121113, -5201871, -10389905, 15427821, -27509937, -15992507, 21670947, 4486675, -5931810, -14466380 },
+ { 16166486, -9483733, -11104130, 6023908, -31926798, -1364923, 2340060, -16254968, -10735770, -10039824 },
+ { 28042865, -3557089, -12126526, 12259706, -3717498, -6945899, 6766453, -8689599, 18036436, 5803270 },
+ },
+ },
+ {
+ {
+ { -817581, 6763912, 11803561, 1585585, 10958447, -2671165, 23855391, 4598332, -6159431, -14117438 },
+ { -31031306, -14256194, 17332029, -2383520, 31312682, -5967183, 696309, 50292, -20095739, 11763584 },
+ { -594563, -2514283, -32234153, 12643980, 12650761, 14811489, 665117, -12613632, -19773211, -10713562 },
+ },
+ {
+ { 30464590, -11262872, -4127476, -12734478, 19835327, -7105613, -24396175, 2075773, -17020157, 992471 },
+ { 18357185, -6994433, 7766382, 16342475, -29324918, 411174, 14578841, 8080033, -11574335, -10601610 },
+ { 19598397, 10334610, 12555054, 2555664, 18821899, -10339780, 21873263, 16014234, 26224780, 16452269 },
+ },
+ {
+ { -30223925, 5145196, 5944548, 16385966, 3976735, 2009897, -11377804, -7618186, -20533829, 3698650 },
+ { 14187449, 3448569, -10636236, -10810935, -22663880, -3433596, 7268410, -10890444, 27394301, 12015369 },
+ { 19695761, 16087646, 28032085, 12999827, 6817792, 11427614, 20244189, -1312777, -13259127, -3402461 },
+ },
+ {
+ { 30860103, 12735208, -1888245, -4699734, -16974906, 2256940, -8166013, 12298312, -8550524, -10393462 },
+ { -5719826, -11245325, -1910649, 15569035, 26642876, -7587760, -5789354, -15118654, -4976164, 12651793 },
+ { -2848395, 9953421, 11531313, -5282879, 26895123, -12697089, -13118820, -16517902, 9768698, -2533218 },
+ },
+ {
+ { -24719459, 1894651, -287698, -4704085, 15348719, -8156530, 32767513, 12765450, 4940095, 10678226 },
+ { 18860224, 15980149, -18987240, -1562570, -26233012, -11071856, -7843882, 13944024, -24372348, 16582019 },
+ { -15504260, 4970268, -29893044, 4175593, -20993212, -2199756, -11704054, 15444560, -11003761, 7989037 },
+ },
+ {
+ { 31490452, 5568061, -2412803, 2182383, -32336847, 4531686, -32078269, 6200206, -19686113, -14800171 },
+ { -17308668, -15879940, -31522777, -2831, -32887382, 16375549, 8680158, -16371713, 28550068, -6857132 },
+ { -28126887, -5688091, 16837845, -1820458, -6850681, 12700016, -30039981, 4364038, 1155602, 5988841 },
+ },
+ {
+ { 21890435, -13272907, -12624011, 12154349, -7831873, 15300496, 23148983, -4470481, 24618407, 8283181 },
+ { -33136107, -10512751, 9975416, 6841041, -31559793, 16356536, 3070187, -7025928, 1466169, 10740210 },
+ { -1509399, -15488185, -13503385, -10655916, 32799044, 909394, -13938903, -5779719, -32164649, -15327040 },
+ },
+ {
+ { 3960823, -14267803, -28026090, -15918051, -19404858, 13146868, 15567327, 951507, -3260321, -573935 },
+ { 24740841, 5052253, -30094131, 8961361, 25877428, 6165135, -24368180, 14397372, -7380369, -6144105 },
+ { -28888365, 3510803, -28103278, -1158478, -11238128, -10631454, -15441463, -14453128, -1625486, -6494814 },
+ },
+ },
+ {
+ {
+ { 793299, -9230478, 8836302, -6235707, -27360908, -2369593, 33152843, -4885251, -9906200, -621852 },
+ { 5666233, 525582, 20782575, -8038419, -24538499, 14657740, 16099374, 1468826, -6171428, -15186581 },
+ { -4859255, -3779343, -2917758, -6748019, 7778750, 11688288, -30404353, -9871238, -1558923, -9863646 },
+ },
+ {
+ { 10896332, -7719704, 824275, 472601, -19460308, 3009587, 25248958, 14783338, -30581476, -15757844 },
+ { 10566929, 12612572, -31944212, 11118703, -12633376, 12362879, 21752402, 8822496, 24003793, 14264025 },
+ { 27713862, -7355973, -11008240, 9227530, 27050101, 2504721, 23886875, -13117525, 13958495, -5732453 },
+ },
+ {
+ { -23481610, 4867226, -27247128, 3900521, 29838369, -8212291, -31889399, -10041781, 7340521, -15410068 },
+ { 4646514, -8011124, -22766023, -11532654, 23184553, 8566613, 31366726, -1381061, -15066784, -10375192 },
+ { -17270517, 12723032, -16993061, 14878794, 21619651, -6197576, 27584817, 3093888, -8843694, 3849921 },
+ },
+ {
+ { -9064912, 2103172, 25561640, -15125738, -5239824, 9582958, 32477045, -9017955, 5002294, -15550259 },
+ { -12057553, -11177906, 21115585, -13365155, 8808712, -12030708, 16489530, 13378448, -25845716, 12741426 },
+ { -5946367, 10645103, -30911586, 15390284, -3286982, -7118677, 24306472, 15852464, 28834118, -7646072 },
+ },
+ {
+ { -17335748, -9107057, -24531279, 9434953, -8472084, -583362, -13090771, 455841, 20461858, 5491305 },
+ { 13669248, -16095482, -12481974, -10203039, -14569770, -11893198, -24995986, 11293807, -28588204, -9421832 },
+ { 28497928, 6272777, -33022994, 14470570, 8906179, -1225630, 18504674, -14165166, 29867745, -8795943 },
+ },
+ {
+ { -16207023, 13517196, -27799630, -13697798, 24009064, -6373891, -6367600, -13175392, 22853429, -4012011 },
+ { 24191378, 16712145, -13931797, 15217831, 14542237, 1646131, 18603514, -11037887, 12876623, -2112447 },
+ { 17902668, 4518229, -411702, -2829247, 26878217, 5258055, -12860753, 608397, 16031844, 3723494 },
+ },
+ {
+ { -28632773, 12763728, -20446446, 7577504, 33001348, -13017745, 17558842, -7872890, 23896954, -4314245 },
+ { -20005381, -12011952, 31520464, 605201, 2543521, 5991821, -2945064, 7229064, -9919646, -8826859 },
+ { 28816045, 298879, -28165016, -15920938, 19000928, -1665890, -12680833, -2949325, -18051778, -2082915 },
+ },
+ {
+ { 16000882, -344896, 3493092, -11447198, -29504595, -13159789, 12577740, 16041268, -19715240, 7847707 },
+ { 10151868, 10572098, 27312476, 7922682, 14825339, 4723128, -32855931, -6519018, -10020567, 3852848 },
+ { -11430470, 15697596, -21121557, -4420647, 5386314, 15063598, 16514493, -15932110, 29330899, -15076224 },
+ },
+ },
+ {
+ {
+ { -25499735, -4378794, -15222908, -6901211, 16615731, 2051784, 3303702, 15490, -27548796, 12314391 },
+ { 15683520, -6003043, 18109120, -9980648, 15337968, -5997823, -16717435, 15921866, 16103996, -3731215 },
+ { -23169824, -10781249, 13588192, -1628807, -3798557, -1074929, -19273607, 5402699, -29815713, -9841101 },
+ },
+ {
+ { 23190676, 2384583, -32714340, 3462154, -29903655, -1529132, -11266856, 8911517, -25205859, 2739713 },
+ { 21374101, -3554250, -33524649, 9874411, 15377179, 11831242, -33529904, 6134907, 4931255, 11987849 },
+ { -7732, -2978858, -16223486, 7277597, 105524, -322051, -31480539, 13861388, -30076310, 10117930 },
+ },
+ {
+ { -29501170, -10744872, -26163768, 13051539, -25625564, 5089643, -6325503, 6704079, 12890019, 15728940 },
+ { -21972360, -11771379, -951059, -4418840, 14704840, 2695116, 903376, -10428139, 12885167, 8311031 },
+ { -17516482, 5352194, 10384213, -13811658, 7506451, 13453191, 26423267, 4384730, 1888765, -5435404 },
+ },
+ {
+ { -25817338, -3107312, -13494599, -3182506, 30896459, -13921729, -32251644, -12707869, -19464434, -3340243 },
+ { -23607977, -2665774, -526091, 4651136, 5765089, 4618330, 6092245, 14845197, 17151279, -9854116 },
+ { -24830458, -12733720, -15165978, 10367250, -29530908, -265356, 22825805, -7087279, -16866484, 16176525 },
+ },
+ {
+ { -23583256, 6564961, 20063689, 3798228, -4740178, 7359225, 2006182, -10363426, -28746253, -10197509 },
+ { -10626600, -4486402, -13320562, -5125317, 3432136, -6393229, 23632037, -1940610, 32808310, 1099883 },
+ { 15030977, 5768825, -27451236, -2887299, -6427378, -15361371, -15277896, -6809350, 2051441, -15225865 },
+ },
+ {
+ { -3362323, -7239372, 7517890, 9824992, 23555850, 295369, 5148398, -14154188, -22686354, 16633660 },
+ { 4577086, -16752288, 13249841, -15304328, 19958763, -14537274, 18559670, -10759549, 8402478, -9864273 },
+ { -28406330, -1051581, -26790155, -907698, -17212414, -11030789, 9453451, -14980072, 17983010, 9967138 },
+ },
+ {
+ { -25762494, 6524722, 26585488, 9969270, 24709298, 1220360, -1677990, 7806337, 17507396, 3651560 },
+ { -10420457, -4118111, 14584639, 15971087, -15768321, 8861010, 26556809, -5574557, -18553322, -11357135 },
+ { 2839101, 14284142, 4029895, 3472686, 14402957, 12689363, -26642121, 8459447, -5605463, -7621941 },
+ },
+ {
+ { -4839289, -3535444, 9744961, 2871048, 25113978, 3187018, -25110813, -849066, 17258084, -7977739 },
+ { 18164541, -10595176, -17154882, -1542417, 19237078, -9745295, 23357533, -15217008, 26908270, 12150756 },
+ { -30264870, -7647865, 5112249, -7036672, -1499807, -6974257, 43168, -5537701, -32302074, 16215819 },
+ },
+ },
+ {
+ {
+ { -6898905, 9824394, -12304779, -4401089, -31397141, -6276835, 32574489, 12532905, -7503072, -8675347 },
+ { -27343522, -16515468, -27151524, -10722951, 946346, 16291093, 254968, 7168080, 21676107, -1943028 },
+ { 21260961, -8424752, -16831886, -11920822, -23677961, 3968121, -3651949, -6215466, -3556191, -7913075 },
+ },
+ {
+ { 16544754, 13250366, -16804428, 15546242, -4583003, 12757258, -2462308, -8680336, -18907032, -9662799 },
+ { -2415239, -15577728, 18312303, 4964443, -15272530, -12653564, 26820651, 16690659, 25459437, -4564609 },
+ { -25144690, 11425020, 28423002, -11020557, -6144921, -15826224, 9142795, -2391602, -6432418, -1644817 },
+ },
+ {
+ { -23104652, 6253476, 16964147, -3768872, -25113972, -12296437, -27457225, -16344658, 6335692, 7249989 },
+ { -30333227, 13979675, 7503222, -12368314, -11956721, -4621693, -30272269, 2682242, 25993170, -12478523 },
+ { 4364628, 5930691, 32304656, -10044554, -8054781, 15091131, 22857016, -10598955, 31820368, 15075278 },
+ },
+ {
+ { 31879134, -8918693, 17258761, 90626, -8041836, -4917709, 24162788, -9650886, -17970238, 12833045 },
+ { 19073683, 14851414, -24403169, -11860168, 7625278, 11091125, -19619190, 2074449, -9413939, 14905377 },
+ { 24483667, -11935567, -2518866, -11547418, -1553130, 15355506, -25282080, 9253129, 27628530, -7555480 },
+ },
+ {
+ { 17597607, 8340603, 19355617, 552187, 26198470, -3176583, 4593324, -9157582, -14110875, 15297016 },
+ { 510886, 14337390, -31785257, 16638632, 6328095, 2713355, -20217417, -11864220, 8683221, 2921426 },
+ { 18606791, 11874196, 27155355, -5281482, -24031742, 6265446, -25178240, -1278924, 4674690, 13890525 },
+ },
+ {
+ { 13609624, 13069022, -27372361, -13055908, 24360586, 9592974, 14977157, 9835105, 4389687, 288396 },
+ { 9922506, -519394, 13613107, 5883594, -18758345, -434263, -12304062, 8317628, 23388070, 16052080 },
+ { 12720016, 11937594, -31970060, -5028689, 26900120, 8561328, -20155687, -11632979, -14754271, -10812892 },
+ },
+ {
+ { 15961858, 14150409, 26716931, -665832, -22794328, 13603569, 11829573, 7467844, -28822128, 929275 },
+ { 11038231, -11582396, -27310482, -7316562, -10498527, -16307831, -23479533, -9371869, -21393143, 2465074 },
+ { 20017163, -4323226, 27915242, 1529148, 12396362, 15675764, 13817261, -9658066, 2463391, -4622140 },
+ },
+ {
+ { -16358878, -12663911, -12065183, 4996454, -1256422, 1073572, 9583558, 12851107, 4003896, 12673717 },
+ { -1731589, -15155870, -3262930, 16143082, 19294135, 13385325, 14741514, -9103726, 7903886, 2348101 },
+ { 24536016, -16515207, 12715592, -3862155, 1511293, 10047386, -3842346, -7129159, -28377538, 10048127 },
+ },
+ },
+ {
+ {
+ { -12622226, -6204820, 30718825, 2591312, -10617028, 12192840, 18873298, -7297090, -32297756, 15221632 },
+ { -26478122, -11103864, 11546244, -1852483, 9180880, 7656409, -21343950, 2095755, 29769758, 6593415 },
+ { -31994208, -2907461, 4176912, 3264766, 12538965, -868111, 26312345, -6118678, 30958054, 8292160 },
+ },
+ {
+ { 31429822, -13959116, 29173532, 15632448, 12174511, -2760094, 32808831, 3977186, 26143136, -3148876 },
+ { 22648901, 1402143, -22799984, 13746059, 7936347, 365344, -8668633, -1674433, -3758243, -2304625 },
+ { -15491917, 8012313, -2514730, -12702462, -23965846, -10254029, -1612713, -1535569, -16664475, 8194478 },
+ },
+ {
+ { 27338066, -7507420, -7414224, 10140405, -19026427, -6589889, 27277191, 8855376, 28572286, 3005164 },
+ { 26287124, 4821776, 25476601, -4145903, -3764513, -15788984, -18008582, 1182479, -26094821, -13079595 },
+ { -7171154, 3178080, 23970071, 6201893, -17195577, -4489192, -21876275, -13982627, 32208683, -1198248 },
+ },
+ {
+ { -16657702, 2817643, -10286362, 14811298, 6024667, 13349505, -27315504, -10497842, -27672585, -11539858 },
+ { 15941029, -9405932, -21367050, 8062055, 31876073, -238629, -15278393, -1444429, 15397331, -4130193 },
+ { 8934485, -13485467, -23286397, -13423241, -32446090, 14047986, 31170398, -1441021, -27505566, 15087184 },
+ },
+ {
+ { -18357243, -2156491, 24524913, -16677868, 15520427, -6360776, -15502406, 11461896, 16788528, -5868942 },
+ { -1947386, 16013773, 21750665, 3714552, -17401782, -16055433, -3770287, -10323320, 31322514, -11615635 },
+ { 21426655, -5650218, -13648287, -5347537, -28812189, -4920970, -18275391, -14621414, 13040862, -12112948 },
+ },
+ {
+ { 11293895, 12478086, -27136401, 15083750, -29307421, 14748872, 14555558, -13417103, 1613711, 4896935 },
+ { -25894883, 15323294, -8489791, -8057900, 25967126, -13425460, 2825960, -4897045, -23971776, -11267415 },
+ { -15924766, -5229880, -17443532, 6410664, 3622847, 10243618, 20615400, 12405433, -23753030, -8436416 },
+ },
+ {
+ { -7091295, 12556208, -20191352, 9025187, -17072479, 4333801, 4378436, 2432030, 23097949, -566018 },
+ { 4565804, -16025654, 20084412, -7842817, 1724999, 189254, 24767264, 10103221, -18512313, 2424778 },
+ { 366633, -11976806, 8173090, -6890119, 30788634, 5745705, -7168678, 1344109, -3642553, 12412659 },
+ },
+ {
+ { -24001791, 7690286, 14929416, -168257, -32210835, -13412986, 24162697, -15326504, -3141501, 11179385 },
+ { 18289522, -14724954, 8056945, 16430056, -21729724, 7842514, -6001441, -1486897, -18684645, -11443503 },
+ { 476239, 6601091, -6152790, -9723375, 17503545, -4863900, 27672959, 13403813, 11052904, 5219329 },
+ },
+ },
+ {
+ {
+ { 20678546, -8375738, -32671898, 8849123, -5009758, 14574752, 31186971, -3973730, 9014762, -8579056 },
+ { -13644050, -10350239, -15962508, 5075808, -1514661, -11534600, -33102500, 9160280, 8473550, -3256838 },
+ { 24900749, 14435722, 17209120, -15292541, -22592275, 9878983, -7689309, -16335821, -24568481, 11788948 },
+ },
+ {
+ { -3118155, -11395194, -13802089, 14797441, 9652448, -6845904, -20037437, 10410733, -24568470, -1458691 },
+ { -15659161, 16736706, -22467150, 10215878, -9097177, 7563911, 11871841, -12505194, -18513325, 8464118 },
+ { -23400612, 8348507, -14585951, -861714, -3950205, -6373419, 14325289, 8628612, 33313881, -8370517 },
+ },
+ {
+ { -20186973, -4967935, 22367356, 5271547, -1097117, -4788838, -24805667, -10236854, -8940735, -5818269 },
+ { -6948785, -1795212, -32625683, -16021179, 32635414, -7374245, 15989197, -12838188, 28358192, -4253904 },
+ { -23561781, -2799059, -32351682, -1661963, -9147719, 10429267, -16637684, 4072016, -5351664, 5596589 },
+ },
+ {
+ { -28236598, -3390048, 12312896, 6213178, 3117142, 16078565, 29266239, 2557221, 1768301, 15373193 },
+ { -7243358, -3246960, -4593467, -7553353, -127927, -912245, -1090902, -4504991, -24660491, 3442910 },
+ { -30210571, 5124043, 14181784, 8197961, 18964734, -11939093, 22597931, 7176455, -18585478, 13365930 },
+ },
+ {
+ { -7877390, -1499958, 8324673, 4690079, 6261860, 890446, 24538107, -8570186, -9689599, -3031667 },
+ { 25008904, -10771599, -4305031, -9638010, 16265036, 15721635, 683793, -11823784, 15723479, -15163481 },
+ { -9660625, 12374379, -27006999, -7026148, -7724114, -12314514, 11879682, 5400171, 519526, -1235876 },
+ },
+ {
+ { 22258397, -16332233, -7869817, 14613016, -22520255, -2950923, -20353881, 7315967, 16648397, 7605640 },
+ { -8081308, -8464597, -8223311, 9719710, 19259459, -15348212, 23994942, -5281555, -9468848, 4763278 },
+ { -21699244, 9220969, -15730624, 1084137, -25476107, -2852390, 31088447, -7764523, -11356529, 728112 },
+ },
+ {
+ { 26047220, -11751471, -6900323, -16521798, 24092068, 9158119, -4273545, -12555558, -29365436, -5498272 },
+ { 17510331, -322857, 5854289, 8403524, 17133918, -3112612, -28111007, 12327945, 10750447, 10014012 },
+ { -10312768, 3936952, 9156313, -8897683, 16498692, -994647, -27481051, -666732, 3424691, 7540221 },
+ },
+ {
+ { 30322361, -6964110, 11361005, -4143317, 7433304, 4989748, -7071422, -16317219, -9244265, 15258046 },
+ { 13054562, -2779497, 19155474, 469045, -12482797, 4566042, 5631406, 2711395, 1062915, -5136345 },
+ { -19240248, -11254599, -29509029, -7499965, -5835763, 13005411, -6066489, 12194497, 32960380, 1459310 },
+ },
+ },
+ {
+ {
+ { 19852034, 7027924, 23669353, 10020366, 8586503, -6657907, 394197, -6101885, 18638003, -11174937 },
+ { 31395534, 15098109, 26581030, 8030562, -16527914, -5007134, 9012486, -7584354, -6643087, -5442636 },
+ { -9192165, -2347377, -1997099, 4529534, 25766844, 607986, -13222, 9677543, -32294889, -6456008 },
+ },
+ {
+ { -2444496, -149937, 29348902, 8186665, 1873760, 12489863, -30934579, -7839692, -7852844, -8138429 },
+ { -15236356, -15433509, 7766470, 746860, 26346930, -10221762, -27333451, 10754588, -9431476, 5203576 },
+ { 31834314, 14135496, -770007, 5159118, 20917671, -16768096, -7467973, -7337524, 31809243, 7347066 },
+ },
+ {
+ { -9606723, -11874240, 20414459, 13033986, 13716524, -11691881, 19797970, -12211255, 15192876, -2087490 },
+ { -12663563, -2181719, 1168162, -3804809, 26747877, -14138091, 10609330, 12694420, 33473243, -13382104 },
+ { 33184999, 11180355, 15832085, -11385430, -1633671, 225884, 15089336, -11023903, -6135662, 14480053 },
+ },
+ {
+ { 31308717, -5619998, 31030840, -1897099, 15674547, -6582883, 5496208, 13685227, 27595050, 8737275 },
+ { -20318852, -15150239, 10933843, -16178022, 8335352, -7546022, -31008351, -12610604, 26498114, 66511 },
+ { 22644454, -8761729, -16671776, 4884562, -3105614, -13559366, 30540766, -4286747, -13327787, -7515095 },
+ },
+ {
+ { -28017847, 9834845, 18617207, -2681312, -3401956, -13307506, 8205540, 13585437, -17127465, 15115439 },
+ { 23711543, -672915, 31206561, -8362711, 6164647, -9709987, -33535882, -1426096, 8236921, 16492939 },
+ { -23910559, -13515526, -26299483, -4503841, 25005590, -7687270, 19574902, 10071562, 6708380, -6222424 },
+ },
+ {
+ { 2101391, -4930054, 19702731, 2367575, -15427167, 1047675, 5301017, 9328700, 29955601, -11678310 },
+ { 3096359, 9271816, -21620864, -15521844, -14847996, -7592937, -25892142, -12635595, -9917575, 6216608 },
+ { -32615849, 338663, -25195611, 2510422, -29213566, -13820213, 24822830, -6146567, -26767480, 7525079 },
+ },
+ {
+ { -23066649, -13985623, 16133487, -7896178, -3389565, 778788, -910336, -2782495, -19386633, 11994101 },
+ { 21691500, -13624626, -641331, -14367021, 3285881, -3483596, -25064666, 9718258, -7477437, 13381418 },
+ { 18445390, -4202236, 14979846, 11622458, -1727110, -3582980, 23111648, -6375247, 28535282, 15779576 },
+ },
+ {
+ { 30098053, 3089662, -9234387, 16662135, -21306940, 11308411, -14068454, 12021730, 9955285, -16303356 },
+ { 9734894, -14576830, -7473633, -9138735, 2060392, 11313496, -18426029, 9924399, 20194861, 13380996 },
+ { -26378102, -7965207, -22167821, 15789297, -18055342, -6168792, -1984914, 15707771, 26342023, 10146099 },
+ },
+ },
+ {
+ {
+ { -26016874, -219943, 21339191, -41388, 19745256, -2878700, -29637280, 2227040, 21612326, -545728 },
+ { -13077387, 1184228, 23562814, -5970442, -20351244, -6348714, 25764461, 12243797, -20856566, 11649658 },
+ { -10031494, 11262626, 27384172, 2271902, 26947504, -15997771, 39944, 6114064, 33514190, 2333242 },
+ },
+ {
+ { -21433588, -12421821, 8119782, 7219913, -21830522, -9016134, -6679750, -12670638, 24350578, -13450001 },
+ { -4116307, -11271533, -23886186, 4843615, -30088339, 690623, -31536088, -10406836, 8317860, 12352766 },
+ { 18200138, -14475911, -33087759, -2696619, -23702521, -9102511, -23552096, -2287550, 20712163, 6719373 },
+ },
+ {
+ { 26656208, 6075253, -7858556, 1886072, -28344043, 4262326, 11117530, -3763210, 26224235, -3297458 },
+ { -17168938, -14854097, -3395676, -16369877, -19954045, 14050420, 21728352, 9493610, 18620611, -16428628 },
+ { -13323321, 13325349, 11432106, 5964811, 18609221, 6062965, -5269471, -9725556, -30701573, -16479657 },
+ },
+ {
+ { -23860538, -11233159, 26961357, 1640861, -32413112, -16737940, 12248509, -5240639, 13735342, 1934062 },
+ { 25089769, 6742589, 17081145, -13406266, 21909293, -16067981, -15136294, -3765346, -21277997, 5473616 },
+ { 31883677, -7961101, 1083432, -11572403, 22828471, 13290673, -7125085, 12469656, 29111212, -5451014 },
+ },
+ {
+ { 24244947, -15050407, -26262976, 2791540, -14997599, 16666678, 24367466, 6388839, -10295587, 452383 },
+ { -25640782, -3417841, 5217916, 16224624, 19987036, -4082269, -24236251, -5915248, 15766062, 8407814 },
+ { -20406999, 13990231, 15495425, 16395525, 5377168, 15166495, -8917023, -4388953, -8067909, 2276718 },
+ },
+ {
+ { 30157918, 12924066, -17712050, 9245753, 19895028, 3368142, -23827587, 5096219, 22740376, -7303417 },
+ { 2041139, -14256350, 7783687, 13876377, -25946985, -13352459, 24051124, 13742383, -15637599, 13295222 },
+ { 33338237, -8505733, 12532113, 7977527, 9106186, -1715251, -17720195, -4612972, -4451357, -14669444 },
+ },
+ {
+ { -20045281, 5454097, -14346548, 6447146, 28862071, 1883651, -2469266, -4141880, 7770569, 9620597 },
+ { 23208068, 7979712, 33071466, 8149229, 1758231, -10834995, 30945528, -1694323, -33502340, -14767970 },
+ { 1439958, -16270480, -1079989, -793782, 4625402, 10647766, -5043801, 1220118, 30494170, -11440799 },
+ },
+ {
+ { -5037580, -13028295, -2970559, -3061767, 15640974, -6701666, -26739026, 926050, -1684339, -13333647 },
+ { 13908495, -3549272, 30919928, -6273825, -21521863, 7989039, 9021034, 9078865, 3353509, 4033511 },
+ { -29663431, -15113610, 32259991, -344482, 24295849, -12912123, 23161163, 8839127, 27485041, 7356032 },
+ },
+ },
+ {
+ {
+ { 9661027, 705443, 11980065, -5370154, -1628543, 14661173, -6346142, 2625015, 28431036, -16771834 },
+ { -23839233, -8311415, -25945511, 7480958, -17681669, -8354183, -22545972, 14150565, 15970762, 4099461 },
+ { 29262576, 16756590, 26350592, -8793563, 8529671, -11208050, 13617293, -9937143, 11465739, 8317062 },
+ },
+ {
+ { -25493081, -6962928, 32500200, -9419051, -23038724, -2302222, 14898637, 3848455, 20969334, -5157516 },
+ { -20384450, -14347713, -18336405, 13884722, -33039454, 2842114, -21610826, -3649888, 11177095, 14989547 },
+ { -24496721, -11716016, 16959896, 2278463, 12066309, 10137771, 13515641, 2581286, -28487508, 9930240 },
+ },
+ {
+ { -17751622, -2097826, 16544300, -13009300, -15914807, -14949081, 18345767, -13403753, 16291481, -5314038 },
+ { -33229194, 2553288, 32678213, 9875984, 8534129, 6889387, -9676774, 6957617, 4368891, 9788741 },
+ { 16660756, 7281060, -10830758, 12911820, 20108584, -8101676, -21722536, -8613148, 16250552, -11111103 },
+ },
+ {
+ { -19765507, 2390526, -16551031, 14161980, 1905286, 6414907, 4689584, 10604807, -30190403, 4782747 },
+ { -1354539, 14736941, -7367442, -13292886, 7710542, -14155590, -9981571, 4383045, 22546403, 437323 },
+ { 31665577, -12180464, -16186830, 1491339, -18368625, 3294682, 27343084, 2786261, -30633590, -14097016 },
+ },
+ {
+ { -14467279, -683715, -33374107, 7448552, 19294360, 14334329, -19690631, 2355319, -19284671, -6114373 },
+ { 15121312, -15796162, 6377020, -6031361, -10798111, -12957845, 18952177, 15496498, -29380133, 11754228 },
+ { -2637277, -13483075, 8488727, -14303896, 12728761, -1622493, 7141596, 11724556, 22761615, -10134141 },
+ },
+ {
+ { 16918416, 11729663, -18083579, 3022987, -31015732, -13339659, -28741185, -12227393, 32851222, 11717399 },
+ { 11166634, 7338049, -6722523, 4531520, -29468672, -7302055, 31474879, 3483633, -1193175, -4030831 },
+ { -185635, 9921305, 31456609, -13536438, -12013818, 13348923, 33142652, 6546660, -19985279, -3948376 },
+ },
+ {
+ { -32460596, 11266712, -11197107, -7899103, 31703694, 3855903, -8537131, -12833048, -30772034, -15486313 },
+ { -18006477, 12709068, 3991746, -6479188, -21491523, -10550425, -31135347, -16049879, 10928917, 3011958 },
+ { -6957757, -15594337, 31696059, 334240, 29576716, 14796075, -30831056, -12805180, 18008031, 10258577 },
+ },
+ {
+ { -22448644, 15655569, 7018479, -4410003, -30314266, -1201591, -1853465, 1367120, 25127874, 6671743 },
+ { 29701166, -14373934, -10878120, 9279288, -17568, 13127210, 21382910, 11042292, 25838796, 4642684 },
+ { -20430234, 14955537, -24126347, 8124619, -5369288, -5990470, 30468147, -13900640, 18423289, 4177476 },
+ },
+ },
+};
\ No newline at end of file
--- /dev/null
+#include "fixedint.h"
+#include "sc.h"
+
+#ifndef ED25519_LOAD_BYTES
+#define ED25519_LOAD_BYTES
+
+static uint64_t load_3(const unsigned char *in) {
+ uint64_t result;
+
+ result = (uint64_t) in[0];
+ result |= ((uint64_t) in[1]) << 8;
+ result |= ((uint64_t) in[2]) << 16;
+
+ return result;
+}
+
+static uint64_t load_4(const unsigned char *in) {
+ uint64_t result;
+
+ result = (uint64_t) in[0];
+ result |= ((uint64_t) in[1]) << 8;
+ result |= ((uint64_t) in[2]) << 16;
+ result |= ((uint64_t) in[3]) << 24;
+
+ return result;
+}
+
+#endif
+
+/*
+Input:
+ s[0]+256*s[1]+...+256^63*s[63] = s
+
+Output:
+ s[0]+256*s[1]+...+256^31*s[31] = s mod l
+ where l = 2^252 + 27742317777372353535851937790883648493.
+ Overwrites s in place.
+*/
+
+void sc_reduce(unsigned char *s) {
+ int64_t s0 = 2097151 & load_3(s);
+ int64_t s1 = 2097151 & (load_4(s + 2) >> 5);
+ int64_t s2 = 2097151 & (load_3(s + 5) >> 2);
+ int64_t s3 = 2097151 & (load_4(s + 7) >> 7);
+ int64_t s4 = 2097151 & (load_4(s + 10) >> 4);
+ int64_t s5 = 2097151 & (load_3(s + 13) >> 1);
+ int64_t s6 = 2097151 & (load_4(s + 15) >> 6);
+ int64_t s7 = 2097151 & (load_3(s + 18) >> 3);
+ int64_t s8 = 2097151 & load_3(s + 21);
+ int64_t s9 = 2097151 & (load_4(s + 23) >> 5);
+ int64_t s10 = 2097151 & (load_3(s + 26) >> 2);
+ int64_t s11 = 2097151 & (load_4(s + 28) >> 7);
+ int64_t s12 = 2097151 & (load_4(s + 31) >> 4);
+ int64_t s13 = 2097151 & (load_3(s + 34) >> 1);
+ int64_t s14 = 2097151 & (load_4(s + 36) >> 6);
+ int64_t s15 = 2097151 & (load_3(s + 39) >> 3);
+ int64_t s16 = 2097151 & load_3(s + 42);
+ int64_t s17 = 2097151 & (load_4(s + 44) >> 5);
+ int64_t s18 = 2097151 & (load_3(s + 47) >> 2);
+ int64_t s19 = 2097151 & (load_4(s + 49) >> 7);
+ int64_t s20 = 2097151 & (load_4(s + 52) >> 4);
+ int64_t s21 = 2097151 & (load_3(s + 55) >> 1);
+ int64_t s22 = 2097151 & (load_4(s + 57) >> 6);
+ int64_t s23 = (load_4(s + 60) >> 3);
+ int64_t carry0;
+ int64_t carry1;
+ int64_t carry2;
+ int64_t carry3;
+ int64_t carry4;
+ int64_t carry5;
+ int64_t carry6;
+ int64_t carry7;
+ int64_t carry8;
+ int64_t carry9;
+ int64_t carry10;
+ int64_t carry11;
+ int64_t carry12;
+ int64_t carry13;
+ int64_t carry14;
+ int64_t carry15;
+ int64_t carry16;
+
+ s11 += s23 * 666643;
+ s12 += s23 * 470296;
+ s13 += s23 * 654183;
+ s14 -= s23 * 997805;
+ s15 += s23 * 136657;
+ s16 -= s23 * 683901;
+ s23 = 0;
+ s10 += s22 * 666643;
+ s11 += s22 * 470296;
+ s12 += s22 * 654183;
+ s13 -= s22 * 997805;
+ s14 += s22 * 136657;
+ s15 -= s22 * 683901;
+ s22 = 0;
+ s9 += s21 * 666643;
+ s10 += s21 * 470296;
+ s11 += s21 * 654183;
+ s12 -= s21 * 997805;
+ s13 += s21 * 136657;
+ s14 -= s21 * 683901;
+ s21 = 0;
+ s8 += s20 * 666643;
+ s9 += s20 * 470296;
+ s10 += s20 * 654183;
+ s11 -= s20 * 997805;
+ s12 += s20 * 136657;
+ s13 -= s20 * 683901;
+ s20 = 0;
+ s7 += s19 * 666643;
+ s8 += s19 * 470296;
+ s9 += s19 * 654183;
+ s10 -= s19 * 997805;
+ s11 += s19 * 136657;
+ s12 -= s19 * 683901;
+ s19 = 0;
+ s6 += s18 * 666643;
+ s7 += s18 * 470296;
+ s8 += s18 * 654183;
+ s9 -= s18 * 997805;
+ s10 += s18 * 136657;
+ s11 -= s18 * 683901;
+ s18 = 0;
+ carry6 = (s6 + (1 << 20)) >> 21;
+ s7 += carry6;
+ s6 -= carry6 << 21;
+ carry8 = (s8 + (1 << 20)) >> 21;
+ s9 += carry8;
+ s8 -= carry8 << 21;
+ carry10 = (s10 + (1 << 20)) >> 21;
+ s11 += carry10;
+ s10 -= carry10 << 21;
+ carry12 = (s12 + (1 << 20)) >> 21;
+ s13 += carry12;
+ s12 -= carry12 << 21;
+ carry14 = (s14 + (1 << 20)) >> 21;
+ s15 += carry14;
+ s14 -= carry14 << 21;
+ carry16 = (s16 + (1 << 20)) >> 21;
+ s17 += carry16;
+ s16 -= carry16 << 21;
+ carry7 = (s7 + (1 << 20)) >> 21;
+ s8 += carry7;
+ s7 -= carry7 << 21;
+ carry9 = (s9 + (1 << 20)) >> 21;
+ s10 += carry9;
+ s9 -= carry9 << 21;
+ carry11 = (s11 + (1 << 20)) >> 21;
+ s12 += carry11;
+ s11 -= carry11 << 21;
+ carry13 = (s13 + (1 << 20)) >> 21;
+ s14 += carry13;
+ s13 -= carry13 << 21;
+ carry15 = (s15 + (1 << 20)) >> 21;
+ s16 += carry15;
+ s15 -= carry15 << 21;
+ s5 += s17 * 666643;
+ s6 += s17 * 470296;
+ s7 += s17 * 654183;
+ s8 -= s17 * 997805;
+ s9 += s17 * 136657;
+ s10 -= s17 * 683901;
+ s17 = 0;
+ s4 += s16 * 666643;
+ s5 += s16 * 470296;
+ s6 += s16 * 654183;
+ s7 -= s16 * 997805;
+ s8 += s16 * 136657;
+ s9 -= s16 * 683901;
+ s16 = 0;
+ s3 += s15 * 666643;
+ s4 += s15 * 470296;
+ s5 += s15 * 654183;
+ s6 -= s15 * 997805;
+ s7 += s15 * 136657;
+ s8 -= s15 * 683901;
+ s15 = 0;
+ s2 += s14 * 666643;
+ s3 += s14 * 470296;
+ s4 += s14 * 654183;
+ s5 -= s14 * 997805;
+ s6 += s14 * 136657;
+ s7 -= s14 * 683901;
+ s14 = 0;
+ s1 += s13 * 666643;
+ s2 += s13 * 470296;
+ s3 += s13 * 654183;
+ s4 -= s13 * 997805;
+ s5 += s13 * 136657;
+ s6 -= s13 * 683901;
+ s13 = 0;
+ s0 += s12 * 666643;
+ s1 += s12 * 470296;
+ s2 += s12 * 654183;
+ s3 -= s12 * 997805;
+ s4 += s12 * 136657;
+ s5 -= s12 * 683901;
+ s12 = 0;
+ carry0 = (s0 + (1 << 20)) >> 21;
+ s1 += carry0;
+ s0 -= carry0 << 21;
+ carry2 = (s2 + (1 << 20)) >> 21;
+ s3 += carry2;
+ s2 -= carry2 << 21;
+ carry4 = (s4 + (1 << 20)) >> 21;
+ s5 += carry4;
+ s4 -= carry4 << 21;
+ carry6 = (s6 + (1 << 20)) >> 21;
+ s7 += carry6;
+ s6 -= carry6 << 21;
+ carry8 = (s8 + (1 << 20)) >> 21;
+ s9 += carry8;
+ s8 -= carry8 << 21;
+ carry10 = (s10 + (1 << 20)) >> 21;
+ s11 += carry10;
+ s10 -= carry10 << 21;
+ carry1 = (s1 + (1 << 20)) >> 21;
+ s2 += carry1;
+ s1 -= carry1 << 21;
+ carry3 = (s3 + (1 << 20)) >> 21;
+ s4 += carry3;
+ s3 -= carry3 << 21;
+ carry5 = (s5 + (1 << 20)) >> 21;
+ s6 += carry5;
+ s5 -= carry5 << 21;
+ carry7 = (s7 + (1 << 20)) >> 21;
+ s8 += carry7;
+ s7 -= carry7 << 21;
+ carry9 = (s9 + (1 << 20)) >> 21;
+ s10 += carry9;
+ s9 -= carry9 << 21;
+ carry11 = (s11 + (1 << 20)) >> 21;
+ s12 += carry11;
+ s11 -= carry11 << 21;
+ s0 += s12 * 666643;
+ s1 += s12 * 470296;
+ s2 += s12 * 654183;
+ s3 -= s12 * 997805;
+ s4 += s12 * 136657;
+ s5 -= s12 * 683901;
+ s12 = 0;
+ carry0 = s0 >> 21;
+ s1 += carry0;
+ s0 -= carry0 << 21;
+ carry1 = s1 >> 21;
+ s2 += carry1;
+ s1 -= carry1 << 21;
+ carry2 = s2 >> 21;
+ s3 += carry2;
+ s2 -= carry2 << 21;
+ carry3 = s3 >> 21;
+ s4 += carry3;
+ s3 -= carry3 << 21;
+ carry4 = s4 >> 21;
+ s5 += carry4;
+ s4 -= carry4 << 21;
+ carry5 = s5 >> 21;
+ s6 += carry5;
+ s5 -= carry5 << 21;
+ carry6 = s6 >> 21;
+ s7 += carry6;
+ s6 -= carry6 << 21;
+ carry7 = s7 >> 21;
+ s8 += carry7;
+ s7 -= carry7 << 21;
+ carry8 = s8 >> 21;
+ s9 += carry8;
+ s8 -= carry8 << 21;
+ carry9 = s9 >> 21;
+ s10 += carry9;
+ s9 -= carry9 << 21;
+ carry10 = s10 >> 21;
+ s11 += carry10;
+ s10 -= carry10 << 21;
+ carry11 = s11 >> 21;
+ s12 += carry11;
+ s11 -= carry11 << 21;
+ s0 += s12 * 666643;
+ s1 += s12 * 470296;
+ s2 += s12 * 654183;
+ s3 -= s12 * 997805;
+ s4 += s12 * 136657;
+ s5 -= s12 * 683901;
+ s12 = 0;
+ carry0 = s0 >> 21;
+ s1 += carry0;
+ s0 -= carry0 << 21;
+ carry1 = s1 >> 21;
+ s2 += carry1;
+ s1 -= carry1 << 21;
+ carry2 = s2 >> 21;
+ s3 += carry2;
+ s2 -= carry2 << 21;
+ carry3 = s3 >> 21;
+ s4 += carry3;
+ s3 -= carry3 << 21;
+ carry4 = s4 >> 21;
+ s5 += carry4;
+ s4 -= carry4 << 21;
+ carry5 = s5 >> 21;
+ s6 += carry5;
+ s5 -= carry5 << 21;
+ carry6 = s6 >> 21;
+ s7 += carry6;
+ s6 -= carry6 << 21;
+ carry7 = s7 >> 21;
+ s8 += carry7;
+ s7 -= carry7 << 21;
+ carry8 = s8 >> 21;
+ s9 += carry8;
+ s8 -= carry8 << 21;
+ carry9 = s9 >> 21;
+ s10 += carry9;
+ s9 -= carry9 << 21;
+ carry10 = s10 >> 21;
+ s11 += carry10;
+ s10 -= carry10 << 21;
+
+ s[0] = (unsigned char) (s0 >> 0);
+ s[1] = (unsigned char) (s0 >> 8);
+ s[2] = (unsigned char) ((s0 >> 16) | (s1 << 5));
+ s[3] = (unsigned char) (s1 >> 3);
+ s[4] = (unsigned char) (s1 >> 11);
+ s[5] = (unsigned char) ((s1 >> 19) | (s2 << 2));
+ s[6] = (unsigned char) (s2 >> 6);
+ s[7] = (unsigned char) ((s2 >> 14) | (s3 << 7));
+ s[8] = (unsigned char) (s3 >> 1);
+ s[9] = (unsigned char) (s3 >> 9);
+ s[10] = (unsigned char) ((s3 >> 17) | (s4 << 4));
+ s[11] = (unsigned char) (s4 >> 4);
+ s[12] = (unsigned char) (s4 >> 12);
+ s[13] = (unsigned char) ((s4 >> 20) | (s5 << 1));
+ s[14] = (unsigned char) (s5 >> 7);
+ s[15] = (unsigned char) ((s5 >> 15) | (s6 << 6));
+ s[16] = (unsigned char) (s6 >> 2);
+ s[17] = (unsigned char) (s6 >> 10);
+ s[18] = (unsigned char) ((s6 >> 18) | (s7 << 3));
+ s[19] = (unsigned char) (s7 >> 5);
+ s[20] = (unsigned char) (s7 >> 13);
+ s[21] = (unsigned char) (s8 >> 0);
+ s[22] = (unsigned char) (s8 >> 8);
+ s[23] = (unsigned char) ((s8 >> 16) | (s9 << 5));
+ s[24] = (unsigned char) (s9 >> 3);
+ s[25] = (unsigned char) (s9 >> 11);
+ s[26] = (unsigned char) ((s9 >> 19) | (s10 << 2));
+ s[27] = (unsigned char) (s10 >> 6);
+ s[28] = (unsigned char) ((s10 >> 14) | (s11 << 7));
+ s[29] = (unsigned char) (s11 >> 1);
+ s[30] = (unsigned char) (s11 >> 9);
+ s[31] = (unsigned char) (s11 >> 17);
+}
+
+
+
+/*
+Input:
+ a[0]+256*a[1]+...+256^31*a[31] = a
+ b[0]+256*b[1]+...+256^31*b[31] = b
+ c[0]+256*c[1]+...+256^31*c[31] = c
+
+Output:
+ s[0]+256*s[1]+...+256^31*s[31] = (ab+c) mod l
+ where l = 2^252 + 27742317777372353535851937790883648493.
+*/
+
+void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b, const unsigned char *c) {
+ int64_t a0 = 2097151 & load_3(a);
+ int64_t a1 = 2097151 & (load_4(a + 2) >> 5);
+ int64_t a2 = 2097151 & (load_3(a + 5) >> 2);
+ int64_t a3 = 2097151 & (load_4(a + 7) >> 7);
+ int64_t a4 = 2097151 & (load_4(a + 10) >> 4);
+ int64_t a5 = 2097151 & (load_3(a + 13) >> 1);
+ int64_t a6 = 2097151 & (load_4(a + 15) >> 6);
+ int64_t a7 = 2097151 & (load_3(a + 18) >> 3);
+ int64_t a8 = 2097151 & load_3(a + 21);
+ int64_t a9 = 2097151 & (load_4(a + 23) >> 5);
+ int64_t a10 = 2097151 & (load_3(a + 26) >> 2);
+ int64_t a11 = (load_4(a + 28) >> 7);
+ int64_t b0 = 2097151 & load_3(b);
+ int64_t b1 = 2097151 & (load_4(b + 2) >> 5);
+ int64_t b2 = 2097151 & (load_3(b + 5) >> 2);
+ int64_t b3 = 2097151 & (load_4(b + 7) >> 7);
+ int64_t b4 = 2097151 & (load_4(b + 10) >> 4);
+ int64_t b5 = 2097151 & (load_3(b + 13) >> 1);
+ int64_t b6 = 2097151 & (load_4(b + 15) >> 6);
+ int64_t b7 = 2097151 & (load_3(b + 18) >> 3);
+ int64_t b8 = 2097151 & load_3(b + 21);
+ int64_t b9 = 2097151 & (load_4(b + 23) >> 5);
+ int64_t b10 = 2097151 & (load_3(b + 26) >> 2);
+ int64_t b11 = (load_4(b + 28) >> 7);
+ int64_t c0 = 2097151 & load_3(c);
+ int64_t c1 = 2097151 & (load_4(c + 2) >> 5);
+ int64_t c2 = 2097151 & (load_3(c + 5) >> 2);
+ int64_t c3 = 2097151 & (load_4(c + 7) >> 7);
+ int64_t c4 = 2097151 & (load_4(c + 10) >> 4);
+ int64_t c5 = 2097151 & (load_3(c + 13) >> 1);
+ int64_t c6 = 2097151 & (load_4(c + 15) >> 6);
+ int64_t c7 = 2097151 & (load_3(c + 18) >> 3);
+ int64_t c8 = 2097151 & load_3(c + 21);
+ int64_t c9 = 2097151 & (load_4(c + 23) >> 5);
+ int64_t c10 = 2097151 & (load_3(c + 26) >> 2);
+ int64_t c11 = (load_4(c + 28) >> 7);
+ int64_t s0;
+ int64_t s1;
+ int64_t s2;
+ int64_t s3;
+ int64_t s4;
+ int64_t s5;
+ int64_t s6;
+ int64_t s7;
+ int64_t s8;
+ int64_t s9;
+ int64_t s10;
+ int64_t s11;
+ int64_t s12;
+ int64_t s13;
+ int64_t s14;
+ int64_t s15;
+ int64_t s16;
+ int64_t s17;
+ int64_t s18;
+ int64_t s19;
+ int64_t s20;
+ int64_t s21;
+ int64_t s22;
+ int64_t s23;
+ int64_t carry0;
+ int64_t carry1;
+ int64_t carry2;
+ int64_t carry3;
+ int64_t carry4;
+ int64_t carry5;
+ int64_t carry6;
+ int64_t carry7;
+ int64_t carry8;
+ int64_t carry9;
+ int64_t carry10;
+ int64_t carry11;
+ int64_t carry12;
+ int64_t carry13;
+ int64_t carry14;
+ int64_t carry15;
+ int64_t carry16;
+ int64_t carry17;
+ int64_t carry18;
+ int64_t carry19;
+ int64_t carry20;
+ int64_t carry21;
+ int64_t carry22;
+
+ s0 = c0 + a0 * b0;
+ s1 = c1 + a0 * b1 + a1 * b0;
+ s2 = c2 + a0 * b2 + a1 * b1 + a2 * b0;
+ s3 = c3 + a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0;
+ s4 = c4 + a0 * b4 + a1 * b3 + a2 * b2 + a3 * b1 + a4 * b0;
+ s5 = c5 + a0 * b5 + a1 * b4 + a2 * b3 + a3 * b2 + a4 * b1 + a5 * b0;
+ s6 = c6 + a0 * b6 + a1 * b5 + a2 * b4 + a3 * b3 + a4 * b2 + a5 * b1 + a6 * b0;
+ s7 = c7 + a0 * b7 + a1 * b6 + a2 * b5 + a3 * b4 + a4 * b3 + a5 * b2 + a6 * b1 + a7 * b0;
+ s8 = c8 + a0 * b8 + a1 * b7 + a2 * b6 + a3 * b5 + a4 * b4 + a5 * b3 + a6 * b2 + a7 * b1 + a8 * b0;
+ s9 = c9 + a0 * b9 + a1 * b8 + a2 * b7 + a3 * b6 + a4 * b5 + a5 * b4 + a6 * b3 + a7 * b2 + a8 * b1 + a9 * b0;
+ s10 = c10 + a0 * b10 + a1 * b9 + a2 * b8 + a3 * b7 + a4 * b6 + a5 * b5 + a6 * b4 + a7 * b3 + a8 * b2 + a9 * b1 + a10 * b0;
+ s11 = c11 + a0 * b11 + a1 * b10 + a2 * b9 + a3 * b8 + a4 * b7 + a5 * b6 + a6 * b5 + a7 * b4 + a8 * b3 + a9 * b2 + a10 * b1 + a11 * b0;
+ s12 = a1 * b11 + a2 * b10 + a3 * b9 + a4 * b8 + a5 * b7 + a6 * b6 + a7 * b5 + a8 * b4 + a9 * b3 + a10 * b2 + a11 * b1;
+ s13 = a2 * b11 + a3 * b10 + a4 * b9 + a5 * b8 + a6 * b7 + a7 * b6 + a8 * b5 + a9 * b4 + a10 * b3 + a11 * b2;
+ s14 = a3 * b11 + a4 * b10 + a5 * b9 + a6 * b8 + a7 * b7 + a8 * b6 + a9 * b5 + a10 * b4 + a11 * b3;
+ s15 = a4 * b11 + a5 * b10 + a6 * b9 + a7 * b8 + a8 * b7 + a9 * b6 + a10 * b5 + a11 * b4;
+ s16 = a5 * b11 + a6 * b10 + a7 * b9 + a8 * b8 + a9 * b7 + a10 * b6 + a11 * b5;
+ s17 = a6 * b11 + a7 * b10 + a8 * b9 + a9 * b8 + a10 * b7 + a11 * b6;
+ s18 = a7 * b11 + a8 * b10 + a9 * b9 + a10 * b8 + a11 * b7;
+ s19 = a8 * b11 + a9 * b10 + a10 * b9 + a11 * b8;
+ s20 = a9 * b11 + a10 * b10 + a11 * b9;
+ s21 = a10 * b11 + a11 * b10;
+ s22 = a11 * b11;
+ s23 = 0;
+ carry0 = (s0 + (1 << 20)) >> 21;
+ s1 += carry0;
+ s0 -= carry0 << 21;
+ carry2 = (s2 + (1 << 20)) >> 21;
+ s3 += carry2;
+ s2 -= carry2 << 21;
+ carry4 = (s4 + (1 << 20)) >> 21;
+ s5 += carry4;
+ s4 -= carry4 << 21;
+ carry6 = (s6 + (1 << 20)) >> 21;
+ s7 += carry6;
+ s6 -= carry6 << 21;
+ carry8 = (s8 + (1 << 20)) >> 21;
+ s9 += carry8;
+ s8 -= carry8 << 21;
+ carry10 = (s10 + (1 << 20)) >> 21;
+ s11 += carry10;
+ s10 -= carry10 << 21;
+ carry12 = (s12 + (1 << 20)) >> 21;
+ s13 += carry12;
+ s12 -= carry12 << 21;
+ carry14 = (s14 + (1 << 20)) >> 21;
+ s15 += carry14;
+ s14 -= carry14 << 21;
+ carry16 = (s16 + (1 << 20)) >> 21;
+ s17 += carry16;
+ s16 -= carry16 << 21;
+ carry18 = (s18 + (1 << 20)) >> 21;
+ s19 += carry18;
+ s18 -= carry18 << 21;
+ carry20 = (s20 + (1 << 20)) >> 21;
+ s21 += carry20;
+ s20 -= carry20 << 21;
+ carry22 = (s22 + (1 << 20)) >> 21;
+ s23 += carry22;
+ s22 -= carry22 << 21;
+ carry1 = (s1 + (1 << 20)) >> 21;
+ s2 += carry1;
+ s1 -= carry1 << 21;
+ carry3 = (s3 + (1 << 20)) >> 21;
+ s4 += carry3;
+ s3 -= carry3 << 21;
+ carry5 = (s5 + (1 << 20)) >> 21;
+ s6 += carry5;
+ s5 -= carry5 << 21;
+ carry7 = (s7 + (1 << 20)) >> 21;
+ s8 += carry7;
+ s7 -= carry7 << 21;
+ carry9 = (s9 + (1 << 20)) >> 21;
+ s10 += carry9;
+ s9 -= carry9 << 21;
+ carry11 = (s11 + (1 << 20)) >> 21;
+ s12 += carry11;
+ s11 -= carry11 << 21;
+ carry13 = (s13 + (1 << 20)) >> 21;
+ s14 += carry13;
+ s13 -= carry13 << 21;
+ carry15 = (s15 + (1 << 20)) >> 21;
+ s16 += carry15;
+ s15 -= carry15 << 21;
+ carry17 = (s17 + (1 << 20)) >> 21;
+ s18 += carry17;
+ s17 -= carry17 << 21;
+ carry19 = (s19 + (1 << 20)) >> 21;
+ s20 += carry19;
+ s19 -= carry19 << 21;
+ carry21 = (s21 + (1 << 20)) >> 21;
+ s22 += carry21;
+ s21 -= carry21 << 21;
+ s11 += s23 * 666643;
+ s12 += s23 * 470296;
+ s13 += s23 * 654183;
+ s14 -= s23 * 997805;
+ s15 += s23 * 136657;
+ s16 -= s23 * 683901;
+ s23 = 0;
+ s10 += s22 * 666643;
+ s11 += s22 * 470296;
+ s12 += s22 * 654183;
+ s13 -= s22 * 997805;
+ s14 += s22 * 136657;
+ s15 -= s22 * 683901;
+ s22 = 0;
+ s9 += s21 * 666643;
+ s10 += s21 * 470296;
+ s11 += s21 * 654183;
+ s12 -= s21 * 997805;
+ s13 += s21 * 136657;
+ s14 -= s21 * 683901;
+ s21 = 0;
+ s8 += s20 * 666643;
+ s9 += s20 * 470296;
+ s10 += s20 * 654183;
+ s11 -= s20 * 997805;
+ s12 += s20 * 136657;
+ s13 -= s20 * 683901;
+ s20 = 0;
+ s7 += s19 * 666643;
+ s8 += s19 * 470296;
+ s9 += s19 * 654183;
+ s10 -= s19 * 997805;
+ s11 += s19 * 136657;
+ s12 -= s19 * 683901;
+ s19 = 0;
+ s6 += s18 * 666643;
+ s7 += s18 * 470296;
+ s8 += s18 * 654183;
+ s9 -= s18 * 997805;
+ s10 += s18 * 136657;
+ s11 -= s18 * 683901;
+ s18 = 0;
+ carry6 = (s6 + (1 << 20)) >> 21;
+ s7 += carry6;
+ s6 -= carry6 << 21;
+ carry8 = (s8 + (1 << 20)) >> 21;
+ s9 += carry8;
+ s8 -= carry8 << 21;
+ carry10 = (s10 + (1 << 20)) >> 21;
+ s11 += carry10;
+ s10 -= carry10 << 21;
+ carry12 = (s12 + (1 << 20)) >> 21;
+ s13 += carry12;
+ s12 -= carry12 << 21;
+ carry14 = (s14 + (1 << 20)) >> 21;
+ s15 += carry14;
+ s14 -= carry14 << 21;
+ carry16 = (s16 + (1 << 20)) >> 21;
+ s17 += carry16;
+ s16 -= carry16 << 21;
+ carry7 = (s7 + (1 << 20)) >> 21;
+ s8 += carry7;
+ s7 -= carry7 << 21;
+ carry9 = (s9 + (1 << 20)) >> 21;
+ s10 += carry9;
+ s9 -= carry9 << 21;
+ carry11 = (s11 + (1 << 20)) >> 21;
+ s12 += carry11;
+ s11 -= carry11 << 21;
+ carry13 = (s13 + (1 << 20)) >> 21;
+ s14 += carry13;
+ s13 -= carry13 << 21;
+ carry15 = (s15 + (1 << 20)) >> 21;
+ s16 += carry15;
+ s15 -= carry15 << 21;
+ s5 += s17 * 666643;
+ s6 += s17 * 470296;
+ s7 += s17 * 654183;
+ s8 -= s17 * 997805;
+ s9 += s17 * 136657;
+ s10 -= s17 * 683901;
+ s17 = 0;
+ s4 += s16 * 666643;
+ s5 += s16 * 470296;
+ s6 += s16 * 654183;
+ s7 -= s16 * 997805;
+ s8 += s16 * 136657;
+ s9 -= s16 * 683901;
+ s16 = 0;
+ s3 += s15 * 666643;
+ s4 += s15 * 470296;
+ s5 += s15 * 654183;
+ s6 -= s15 * 997805;
+ s7 += s15 * 136657;
+ s8 -= s15 * 683901;
+ s15 = 0;
+ s2 += s14 * 666643;
+ s3 += s14 * 470296;
+ s4 += s14 * 654183;
+ s5 -= s14 * 997805;
+ s6 += s14 * 136657;
+ s7 -= s14 * 683901;
+ s14 = 0;
+ s1 += s13 * 666643;
+ s2 += s13 * 470296;
+ s3 += s13 * 654183;
+ s4 -= s13 * 997805;
+ s5 += s13 * 136657;
+ s6 -= s13 * 683901;
+ s13 = 0;
+ s0 += s12 * 666643;
+ s1 += s12 * 470296;
+ s2 += s12 * 654183;
+ s3 -= s12 * 997805;
+ s4 += s12 * 136657;
+ s5 -= s12 * 683901;
+ s12 = 0;
+ carry0 = (s0 + (1 << 20)) >> 21;
+ s1 += carry0;
+ s0 -= carry0 << 21;
+ carry2 = (s2 + (1 << 20)) >> 21;
+ s3 += carry2;
+ s2 -= carry2 << 21;
+ carry4 = (s4 + (1 << 20)) >> 21;
+ s5 += carry4;
+ s4 -= carry4 << 21;
+ carry6 = (s6 + (1 << 20)) >> 21;
+ s7 += carry6;
+ s6 -= carry6 << 21;
+ carry8 = (s8 + (1 << 20)) >> 21;
+ s9 += carry8;
+ s8 -= carry8 << 21;
+ carry10 = (s10 + (1 << 20)) >> 21;
+ s11 += carry10;
+ s10 -= carry10 << 21;
+ carry1 = (s1 + (1 << 20)) >> 21;
+ s2 += carry1;
+ s1 -= carry1 << 21;
+ carry3 = (s3 + (1 << 20)) >> 21;
+ s4 += carry3;
+ s3 -= carry3 << 21;
+ carry5 = (s5 + (1 << 20)) >> 21;
+ s6 += carry5;
+ s5 -= carry5 << 21;
+ carry7 = (s7 + (1 << 20)) >> 21;
+ s8 += carry7;
+ s7 -= carry7 << 21;
+ carry9 = (s9 + (1 << 20)) >> 21;
+ s10 += carry9;
+ s9 -= carry9 << 21;
+ carry11 = (s11 + (1 << 20)) >> 21;
+ s12 += carry11;
+ s11 -= carry11 << 21;
+ s0 += s12 * 666643;
+ s1 += s12 * 470296;
+ s2 += s12 * 654183;
+ s3 -= s12 * 997805;
+ s4 += s12 * 136657;
+ s5 -= s12 * 683901;
+ s12 = 0;
+ carry0 = s0 >> 21;
+ s1 += carry0;
+ s0 -= carry0 << 21;
+ carry1 = s1 >> 21;
+ s2 += carry1;
+ s1 -= carry1 << 21;
+ carry2 = s2 >> 21;
+ s3 += carry2;
+ s2 -= carry2 << 21;
+ carry3 = s3 >> 21;
+ s4 += carry3;
+ s3 -= carry3 << 21;
+ carry4 = s4 >> 21;
+ s5 += carry4;
+ s4 -= carry4 << 21;
+ carry5 = s5 >> 21;
+ s6 += carry5;
+ s5 -= carry5 << 21;
+ carry6 = s6 >> 21;
+ s7 += carry6;
+ s6 -= carry6 << 21;
+ carry7 = s7 >> 21;
+ s8 += carry7;
+ s7 -= carry7 << 21;
+ carry8 = s8 >> 21;
+ s9 += carry8;
+ s8 -= carry8 << 21;
+ carry9 = s9 >> 21;
+ s10 += carry9;
+ s9 -= carry9 << 21;
+ carry10 = s10 >> 21;
+ s11 += carry10;
+ s10 -= carry10 << 21;
+ carry11 = s11 >> 21;
+ s12 += carry11;
+ s11 -= carry11 << 21;
+ s0 += s12 * 666643;
+ s1 += s12 * 470296;
+ s2 += s12 * 654183;
+ s3 -= s12 * 997805;
+ s4 += s12 * 136657;
+ s5 -= s12 * 683901;
+ s12 = 0;
+ carry0 = s0 >> 21;
+ s1 += carry0;
+ s0 -= carry0 << 21;
+ carry1 = s1 >> 21;
+ s2 += carry1;
+ s1 -= carry1 << 21;
+ carry2 = s2 >> 21;
+ s3 += carry2;
+ s2 -= carry2 << 21;
+ carry3 = s3 >> 21;
+ s4 += carry3;
+ s3 -= carry3 << 21;
+ carry4 = s4 >> 21;
+ s5 += carry4;
+ s4 -= carry4 << 21;
+ carry5 = s5 >> 21;
+ s6 += carry5;
+ s5 -= carry5 << 21;
+ carry6 = s6 >> 21;
+ s7 += carry6;
+ s6 -= carry6 << 21;
+ carry7 = s7 >> 21;
+ s8 += carry7;
+ s7 -= carry7 << 21;
+ carry8 = s8 >> 21;
+ s9 += carry8;
+ s8 -= carry8 << 21;
+ carry9 = s9 >> 21;
+ s10 += carry9;
+ s9 -= carry9 << 21;
+ carry10 = s10 >> 21;
+ s11 += carry10;
+ s10 -= carry10 << 21;
+
+ s[0] = (unsigned char) (s0 >> 0);
+ s[1] = (unsigned char) (s0 >> 8);
+ s[2] = (unsigned char) ((s0 >> 16) | (s1 << 5));
+ s[3] = (unsigned char) (s1 >> 3);
+ s[4] = (unsigned char) (s1 >> 11);
+ s[5] = (unsigned char) ((s1 >> 19) | (s2 << 2));
+ s[6] = (unsigned char) (s2 >> 6);
+ s[7] = (unsigned char) ((s2 >> 14) | (s3 << 7));
+ s[8] = (unsigned char) (s3 >> 1);
+ s[9] = (unsigned char) (s3 >> 9);
+ s[10] = (unsigned char) ((s3 >> 17) | (s4 << 4));
+ s[11] = (unsigned char) (s4 >> 4);
+ s[12] = (unsigned char) (s4 >> 12);
+ s[13] = (unsigned char) ((s4 >> 20) | (s5 << 1));
+ s[14] = (unsigned char) (s5 >> 7);
+ s[15] = (unsigned char) ((s5 >> 15) | (s6 << 6));
+ s[16] = (unsigned char) (s6 >> 2);
+ s[17] = (unsigned char) (s6 >> 10);
+ s[18] = (unsigned char) ((s6 >> 18) | (s7 << 3));
+ s[19] = (unsigned char) (s7 >> 5);
+ s[20] = (unsigned char) (s7 >> 13);
+ s[21] = (unsigned char) (s8 >> 0);
+ s[22] = (unsigned char) (s8 >> 8);
+ s[23] = (unsigned char) ((s8 >> 16) | (s9 << 5));
+ s[24] = (unsigned char) (s9 >> 3);
+ s[25] = (unsigned char) (s9 >> 11);
+ s[26] = (unsigned char) ((s9 >> 19) | (s10 << 2));
+ s[27] = (unsigned char) (s10 >> 6);
+ s[28] = (unsigned char) ((s10 >> 14) | (s11 << 7));
+ s[29] = (unsigned char) (s11 >> 1);
+ s[30] = (unsigned char) (s11 >> 9);
+ s[31] = (unsigned char) (s11 >> 17);
+}
--- /dev/null
+#ifndef SC_H
+#define SC_H
+
+/*
+The set of scalars is \Z/l
+where l = 2^252 + 27742317777372353535851937790883648493.
+*/
+
+void sc_reduce(unsigned char *s);
+void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b, const unsigned char *c);
+
+#endif
\ No newline at end of file
--- /dev/null
+#include "ed25519.h"
+
+#ifndef ED25519_NO_SEED
+
+#ifdef _WIN32
+#include <Windows.h>
+#include <Wincrypt.h>
+#else
+#include <stdio.h>
+#endif
+
+int ed25519_create_seed(unsigned char *seed) {
+#ifdef _WIN32
+ HCRYPTPROV prov;
+
+ if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
+ return 1;
+ }
+
+ if (!CryptGenRandom(prov, 32, seed)) {
+ CryptReleaseContext(prov, 0);
+ return 1;
+ }
+
+ CryptReleaseContext(prov, 0);
+#else
+ FILE *f = fopen("/dev/urandom", "rb");
+
+ if (f == NULL) {
+ return 1;
+ }
+
+ fread(seed, 1, 32, f);
+ fclose(f);
+#endif
+
+ return 0;
+}
+
+#endif
\ No newline at end of file
--- /dev/null
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+#include "fixedint.h"
+#include "sha512.h"
+
+/* the K array */
+static const uint64_t K[80] = {
+ UINT64_C(0x428a2f98d728ae22), UINT64_C(0x7137449123ef65cd),
+ UINT64_C(0xb5c0fbcfec4d3b2f), UINT64_C(0xe9b5dba58189dbbc),
+ UINT64_C(0x3956c25bf348b538), UINT64_C(0x59f111f1b605d019),
+ UINT64_C(0x923f82a4af194f9b), UINT64_C(0xab1c5ed5da6d8118),
+ UINT64_C(0xd807aa98a3030242), UINT64_C(0x12835b0145706fbe),
+ UINT64_C(0x243185be4ee4b28c), UINT64_C(0x550c7dc3d5ffb4e2),
+ UINT64_C(0x72be5d74f27b896f), UINT64_C(0x80deb1fe3b1696b1),
+ UINT64_C(0x9bdc06a725c71235), UINT64_C(0xc19bf174cf692694),
+ UINT64_C(0xe49b69c19ef14ad2), UINT64_C(0xefbe4786384f25e3),
+ UINT64_C(0x0fc19dc68b8cd5b5), UINT64_C(0x240ca1cc77ac9c65),
+ UINT64_C(0x2de92c6f592b0275), UINT64_C(0x4a7484aa6ea6e483),
+ UINT64_C(0x5cb0a9dcbd41fbd4), UINT64_C(0x76f988da831153b5),
+ UINT64_C(0x983e5152ee66dfab), UINT64_C(0xa831c66d2db43210),
+ UINT64_C(0xb00327c898fb213f), UINT64_C(0xbf597fc7beef0ee4),
+ UINT64_C(0xc6e00bf33da88fc2), UINT64_C(0xd5a79147930aa725),
+ UINT64_C(0x06ca6351e003826f), UINT64_C(0x142929670a0e6e70),
+ UINT64_C(0x27b70a8546d22ffc), UINT64_C(0x2e1b21385c26c926),
+ UINT64_C(0x4d2c6dfc5ac42aed), UINT64_C(0x53380d139d95b3df),
+ UINT64_C(0x650a73548baf63de), UINT64_C(0x766a0abb3c77b2a8),
+ UINT64_C(0x81c2c92e47edaee6), UINT64_C(0x92722c851482353b),
+ UINT64_C(0xa2bfe8a14cf10364), UINT64_C(0xa81a664bbc423001),
+ UINT64_C(0xc24b8b70d0f89791), UINT64_C(0xc76c51a30654be30),
+ UINT64_C(0xd192e819d6ef5218), UINT64_C(0xd69906245565a910),
+ UINT64_C(0xf40e35855771202a), UINT64_C(0x106aa07032bbd1b8),
+ UINT64_C(0x19a4c116b8d2d0c8), UINT64_C(0x1e376c085141ab53),
+ UINT64_C(0x2748774cdf8eeb99), UINT64_C(0x34b0bcb5e19b48a8),
+ UINT64_C(0x391c0cb3c5c95a63), UINT64_C(0x4ed8aa4ae3418acb),
+ UINT64_C(0x5b9cca4f7763e373), UINT64_C(0x682e6ff3d6b2b8a3),
+ UINT64_C(0x748f82ee5defb2fc), UINT64_C(0x78a5636f43172f60),
+ UINT64_C(0x84c87814a1f0ab72), UINT64_C(0x8cc702081a6439ec),
+ UINT64_C(0x90befffa23631e28), UINT64_C(0xa4506cebde82bde9),
+ UINT64_C(0xbef9a3f7b2c67915), UINT64_C(0xc67178f2e372532b),
+ UINT64_C(0xca273eceea26619c), UINT64_C(0xd186b8c721c0c207),
+ UINT64_C(0xeada7dd6cde0eb1e), UINT64_C(0xf57d4f7fee6ed178),
+ UINT64_C(0x06f067aa72176fba), UINT64_C(0x0a637dc5a2c898a6),
+ UINT64_C(0x113f9804bef90dae), UINT64_C(0x1b710b35131c471b),
+ UINT64_C(0x28db77f523047d84), UINT64_C(0x32caab7b40c72493),
+ UINT64_C(0x3c9ebe0a15c9bebc), UINT64_C(0x431d67c49c100d4c),
+ UINT64_C(0x4cc5d4becb3e42b6), UINT64_C(0x597f299cfc657e2a),
+ UINT64_C(0x5fcb6fab3ad6faec), UINT64_C(0x6c44198c4a475817)
+};
+
+/* Various logical functions */
+
+#define ROR64c(x, y) \
+ ( ((((x)&UINT64_C(0xFFFFFFFFFFFFFFFF))>>((uint64_t)(y)&UINT64_C(63))) | \
+ ((x)<<((uint64_t)(64-((y)&UINT64_C(63)))))) & UINT64_C(0xFFFFFFFFFFFFFFFF))
+
+#define STORE64H(x, y) \
+ { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255); \
+ (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255); \
+ (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255); \
+ (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); }
+
+#define LOAD64H(x, y) \
+ { x = (((uint64_t)((y)[0] & 255))<<56)|(((uint64_t)((y)[1] & 255))<<48) | \
+ (((uint64_t)((y)[2] & 255))<<40)|(((uint64_t)((y)[3] & 255))<<32) | \
+ (((uint64_t)((y)[4] & 255))<<24)|(((uint64_t)((y)[5] & 255))<<16) | \
+ (((uint64_t)((y)[6] & 255))<<8)|(((uint64_t)((y)[7] & 255))); }
+
+
+#define Ch(x,y,z) (z ^ (x & (y ^ z)))
+#define Maj(x,y,z) (((x | y) & z) | (x & y))
+#define S(x, n) ROR64c(x, n)
+#define R(x, n) (((x) &UINT64_C(0xFFFFFFFFFFFFFFFF))>>((uint64_t)n))
+#define Sigma0(x) (S(x, 28) ^ S(x, 34) ^ S(x, 39))
+#define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41))
+#define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7))
+#define Gamma1(x) (S(x, 19) ^ S(x, 61) ^ R(x, 6))
+#ifndef MIN
+ #define MIN(x, y) ( ((x)<(y))?(x):(y) )
+#endif
+
+/* compress 1024-bits */
+static int sha512_compress(sha512_context *md, unsigned char *buf)
+{
+ uint64_t S[8], W[80], t0, t1;
+ int i;
+
+ /* copy state into S */
+ for (i = 0; i < 8; i++) {
+ S[i] = md->state[i];
+ }
+
+ /* copy the state into 1024-bits into W[0..15] */
+ for (i = 0; i < 16; i++) {
+ LOAD64H(W[i], buf + (8*i));
+ }
+
+ /* fill W[16..79] */
+ for (i = 16; i < 80; i++) {
+ W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16];
+ }
+
+/* Compress */
+ #define RND(a,b,c,d,e,f,g,h,i) \
+ t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \
+ t1 = Sigma0(a) + Maj(a, b, c);\
+ d += t0; \
+ h = t0 + t1;
+
+ for (i = 0; i < 80; i += 8) {
+ RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i+0);
+ RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],i+1);
+ RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],i+2);
+ RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],i+3);
+ RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],i+4);
+ RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],i+5);
+ RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],i+6);
+ RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],i+7);
+ }
+
+ #undef RND
+
+
+
+ /* feedback */
+ for (i = 0; i < 8; i++) {
+ md->state[i] = md->state[i] + S[i];
+ }
+
+ return 0;
+}
+
+
+/**
+ Initialize the hash state
+ @param md The hash state you wish to initialize
+ @return 0 if successful
+*/
+int sha512_init(sha512_context * md) {
+ if (md == NULL) return 1;
+
+ md->curlen = 0;
+ md->length = 0;
+ md->state[0] = UINT64_C(0x6a09e667f3bcc908);
+ md->state[1] = UINT64_C(0xbb67ae8584caa73b);
+ md->state[2] = UINT64_C(0x3c6ef372fe94f82b);
+ md->state[3] = UINT64_C(0xa54ff53a5f1d36f1);
+ md->state[4] = UINT64_C(0x510e527fade682d1);
+ md->state[5] = UINT64_C(0x9b05688c2b3e6c1f);
+ md->state[6] = UINT64_C(0x1f83d9abfb41bd6b);
+ md->state[7] = UINT64_C(0x5be0cd19137e2179);
+
+ return 0;
+}
+
+/**
+ Process a block of memory though the hash
+ @param md The hash state
+ @param in The data to hash
+ @param inlen The length of the data (octets)
+ @return 0 if successful
+*/
+int sha512_update (sha512_context * md, const unsigned char *in, size_t inlen)
+{
+ size_t n;
+ size_t i;
+ int err;
+ if (md == NULL) return 1;
+ if (in == NULL) return 1;
+ if (md->curlen > sizeof(md->buf)) {
+ return 1;
+ }
+ while (inlen > 0) {
+ if (md->curlen == 0 && inlen >= 128) {
+ if ((err = sha512_compress (md, (unsigned char *)in)) != 0) {
+ return err;
+ }
+ md->length += 128 * 8;
+ in += 128;
+ inlen -= 128;
+ } else {
+ n = MIN(inlen, (128 - md->curlen));
+
+ for (i = 0; i < n; i++) {
+ md->buf[i + md->curlen] = in[i];
+ }
+
+
+ md->curlen += n;
+ in += n;
+ inlen -= n;
+ if (md->curlen == 128) {
+ if ((err = sha512_compress (md, md->buf)) != 0) {
+ return err;
+ }
+ md->length += 8*128;
+ md->curlen = 0;
+ }
+ }
+ }
+ return 0;
+}
+
+/**
+ Terminate the hash to get the digest
+ @param md The hash state
+ @param out [out] The destination of the hash (64 bytes)
+ @return 0 if successful
+*/
+ int sha512_final(sha512_context * md, unsigned char *out)
+ {
+ int i;
+
+ if (md == NULL) return 1;
+ if (out == NULL) return 1;
+
+ if (md->curlen >= sizeof(md->buf)) {
+ return 1;
+ }
+
+ /* increase the length of the message */
+ md->length += md->curlen * UINT64_C(8);
+
+ /* append the '1' bit */
+ md->buf[md->curlen++] = (unsigned char)0x80;
+
+ /* if the length is currently above 112 bytes we append zeros
+ * then compress. Then we can fall back to padding zeros and length
+ * encoding like normal.
+ */
+ if (md->curlen > 112) {
+ while (md->curlen < 128) {
+ md->buf[md->curlen++] = (unsigned char)0;
+ }
+ sha512_compress(md, md->buf);
+ md->curlen = 0;
+ }
+
+ /* pad upto 120 bytes of zeroes
+ * note: that from 112 to 120 is the 64 MSB of the length. We assume that you won't hash
+ * > 2^64 bits of data... :-)
+ */
+while (md->curlen < 120) {
+ md->buf[md->curlen++] = (unsigned char)0;
+}
+
+ /* store length */
+STORE64H(md->length, md->buf+120);
+sha512_compress(md, md->buf);
+
+ /* copy output */
+for (i = 0; i < 8; i++) {
+ STORE64H(md->state[i], out+(8*i));
+}
+
+return 0;
+}
+
+int sha512(const unsigned char *message, size_t message_len, unsigned char *out)
+{
+ sha512_context ctx;
+ int ret;
+ if ((ret = sha512_init(&ctx))) return ret;
+ if ((ret = sha512_update(&ctx, message, message_len))) return ret;
+ if ((ret = sha512_final(&ctx, out))) return ret;
+ return 0;
+}
--- /dev/null
+#ifndef SHA512_H
+#define SHA512_H
+
+#include <stddef.h>
+
+#include "fixedint.h"
+
+/* state */
+typedef struct sha512_context_ {
+ uint64_t length, state[8];
+ size_t curlen;
+ unsigned char buf[128];
+} sha512_context;
+
+
+int sha512_init(sha512_context * md);
+int sha512_final(sha512_context * md, unsigned char *out);
+int sha512_update(sha512_context * md, const unsigned char *in, size_t inlen);
+int sha512(const unsigned char *message, size_t message_len, unsigned char *out);
+
+#endif
\ No newline at end of file
--- /dev/null
+#include "ed25519.h"
+#include "sha512.h"
+#include "ge.h"
+#include "sc.h"
+
+
+void ed25519_sign(unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key, const unsigned char *private_key) {
+ sha512_context hash;
+ unsigned char hram[64];
+ unsigned char r[64];
+ ge_p3 R;
+
+
+ sha512_init(&hash);
+ sha512_update(&hash, private_key + 32, 32);
+ sha512_update(&hash, message, message_len);
+ sha512_final(&hash, r);
+
+ sc_reduce(r);
+ ge_scalarmult_base(&R, r);
+ ge_p3_tobytes(signature, &R);
+
+ sha512_init(&hash);
+ sha512_update(&hash, signature, 32);
+ sha512_update(&hash, public_key, 32);
+ sha512_update(&hash, message, message_len);
+ sha512_final(&hash, hram);
+
+ sc_reduce(hram);
+ sc_muladd(signature + 32, hram, private_key, r);
+}
--- /dev/null
+#include "ed25519.h"
+#include "sha512.h"
+#include "ge.h"
+#include "sc.h"
+
+static int consttime_equal(const unsigned char *x, const unsigned char *y) {
+ unsigned char r = 0;
+
+ r = x[0] ^ y[0];
+ #define F(i) r |= x[i] ^ y[i]
+ F(1);
+ F(2);
+ F(3);
+ F(4);
+ F(5);
+ F(6);
+ F(7);
+ F(8);
+ F(9);
+ F(10);
+ F(11);
+ F(12);
+ F(13);
+ F(14);
+ F(15);
+ F(16);
+ F(17);
+ F(18);
+ F(19);
+ F(20);
+ F(21);
+ F(22);
+ F(23);
+ F(24);
+ F(25);
+ F(26);
+ F(27);
+ F(28);
+ F(29);
+ F(30);
+ F(31);
+ #undef F
+
+ return !r;
+}
+
+int ed25519_verify(const unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key) {
+ unsigned char h[64];
+ unsigned char checker[32];
+ sha512_context hash;
+ ge_p3 A;
+ ge_p2 R;
+
+ if (signature[63] & 224) {
+ return 0;
+ }
+
+ if (ge_frombytes_negate_vartime(&A, public_key) != 0) {
+ return 0;
+ }
+
+ sha512_init(&hash);
+ sha512_update(&hash, signature, 32);
+ sha512_update(&hash, public_key, 32);
+ sha512_update(&hash, message, message_len);
+ sha512_final(&hash, h);
+
+ sc_reduce(h);
+ ge_double_scalarmult_vartime(&R, h, &A, signature + 32);
+ ge_tobytes(checker, &R);
+
+ if (!consttime_equal(checker, signature)) {
+ return 0;
+ }
+
+ return 1;
+}
--- /dev/null
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+/* #define ED25519_DLL */
+#include "src/ed25519.h"
+
+#include "src/ge.h"
+#include "src/sc.h"
+
+
+int main() {
+ unsigned char public_key[32], private_key[64], seed[32], scalar[32];
+ unsigned char other_public_key[32], other_private_key[64];
+ unsigned char shared_secret[32], other_shared_secret[32];
+ unsigned char signature[64];
+
+ clock_t start;
+ clock_t end;
+ int i;
+
+ const unsigned char message[] = "Hello, world!";
+ const int message_len = strlen((char*) message);
+
+ /* create a random seed, and a keypair out of that seed */
+ ed25519_create_seed(seed);
+ ed25519_create_keypair(public_key, private_key, seed);
+
+ /* create signature on the message with the keypair */
+ ed25519_sign(signature, message, message_len, public_key, private_key);
+
+ /* verify the signature */
+ if (ed25519_verify(signature, message, message_len, public_key)) {
+ printf("valid signature\n");
+ } else {
+ printf("invalid signature\n");
+ }
+
+ /* create scalar and add it to the keypair */
+ ed25519_create_seed(scalar);
+ ed25519_add_scalar(public_key, private_key, scalar);
+
+ /* create signature with the new keypair */
+ ed25519_sign(signature, message, message_len, public_key, private_key);
+
+ /* verify the signature with the new keypair */
+ if (ed25519_verify(signature, message, message_len, public_key)) {
+ printf("valid signature\n");
+ } else {
+ printf("invalid signature\n");
+ }
+
+ /* make a slight adjustment and verify again */
+ signature[44] ^= 0x10;
+ if (ed25519_verify(signature, message, message_len, public_key)) {
+ printf("did not detect signature change\n");
+ } else {
+ printf("correctly detected signature change\n");
+ }
+
+ /* generate two keypairs for testing key exchange */
+ ed25519_create_seed(seed);
+ ed25519_create_keypair(public_key, private_key, seed);
+ ed25519_create_seed(seed);
+ ed25519_create_keypair(other_public_key, other_private_key, seed);
+
+ /* create two shared secrets - from both perspectives - and check if they're equal */
+ ed25519_key_exchange(shared_secret, other_public_key, private_key);
+ ed25519_key_exchange(other_shared_secret, public_key, other_private_key);
+
+ for (i = 0; i < 32; ++i) {
+ if (shared_secret[i] != other_shared_secret[i]) {
+ printf("key exchange was incorrect\n");
+ break;
+ }
+ }
+
+ if (i == 32) {
+ printf("key exchange was correct\n");
+ }
+
+ /* test performance */
+ printf("testing seed generation performance: ");
+ start = clock();
+ for (i = 0; i < 10000; ++i) {
+ ed25519_create_seed(seed);
+ }
+ end = clock();
+
+ printf("%fus per seed\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000);
+
+
+ printf("testing key generation performance: ");
+ start = clock();
+ for (i = 0; i < 10000; ++i) {
+ ed25519_create_keypair(public_key, private_key, seed);
+ }
+ end = clock();
+
+ printf("%fus per keypair\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000);
+
+ printf("testing sign performance: ");
+ start = clock();
+ for (i = 0; i < 10000; ++i) {
+ ed25519_sign(signature, message, message_len, public_key, private_key);
+ }
+ end = clock();
+
+ printf("%fus per signature\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000);
+
+ printf("testing verify performance: ");
+ start = clock();
+ for (i = 0; i < 10000; ++i) {
+ ed25519_verify(signature, message, message_len, public_key);
+ }
+ end = clock();
+
+ printf("%fus per signature\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000);
+
+
+ printf("testing keypair scalar addition performance: ");
+ start = clock();
+ for (i = 0; i < 10000; ++i) {
+ ed25519_add_scalar(public_key, private_key, scalar);
+ }
+ end = clock();
+
+ printf("%fus per keypair\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000);
+
+ printf("testing public key scalar addition performance: ");
+ start = clock();
+ for (i = 0; i < 10000; ++i) {
+ ed25519_add_scalar(public_key, NULL, scalar);
+ }
+ end = clock();
+
+ printf("%fus per key\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000);
+
+ printf("testing key exchange performance: ");
+ start = clock();
+ for (i = 0; i < 10000; ++i) {
+ ed25519_key_exchange(shared_secret, other_public_key, private_key);
+ }
+ end = clock();
+
+ printf("%fus per shared secret\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000);
+
+ return 0;
+}
--- /dev/null
+/* Copyright 2015, 2016 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "olm/account.hh"
+#include "olm/base64.hh"
+#include "olm/pickle.h"
+#include "olm/pickle.hh"
+#include "olm/memory.hh"
+
+olm::Account::Account(
+) : num_fallback_keys(0),
+ next_one_time_key_id(0),
+ last_error(OlmErrorCode::OLM_SUCCESS) {
+}
+
+
+olm::OneTimeKey const * olm::Account::lookup_key(
+ _olm_curve25519_public_key const & public_key
+) {
+ for (olm::OneTimeKey const & key : one_time_keys) {
+ if (olm::array_equal(key.key.public_key.public_key, public_key.public_key)) {
+ return &key;
+ }
+ }
+ if (num_fallback_keys >= 1
+ && olm::array_equal(
+ current_fallback_key.key.public_key.public_key, public_key.public_key
+ )
+ ) {
+ return ¤t_fallback_key;
+ }
+ if (num_fallback_keys >= 2
+ && olm::array_equal(
+ prev_fallback_key.key.public_key.public_key, public_key.public_key
+ )
+ ) {
+ return &prev_fallback_key;
+ }
+ return 0;
+}
+
+std::size_t olm::Account::remove_key(
+ _olm_curve25519_public_key const & public_key
+) {
+ OneTimeKey * i;
+ for (i = one_time_keys.begin(); i != one_time_keys.end(); ++i) {
+ if (olm::array_equal(i->key.public_key.public_key, public_key.public_key)) {
+ std::uint32_t id = i->id;
+ one_time_keys.erase(i);
+ return id;
+ }
+ }
+ // check if the key is a fallback key, to avoid returning an error, but
+ // don't actually remove it
+ if (num_fallback_keys >= 1
+ && olm::array_equal(
+ current_fallback_key.key.public_key.public_key, public_key.public_key
+ )
+ ) {
+ return current_fallback_key.id;
+ }
+ if (num_fallback_keys >= 2
+ && olm::array_equal(
+ prev_fallback_key.key.public_key.public_key, public_key.public_key
+ )
+ ) {
+ return prev_fallback_key.id;
+ }
+ return std::size_t(-1);
+}
+
+std::size_t olm::Account::new_account_random_length() const {
+ return ED25519_RANDOM_LENGTH + CURVE25519_RANDOM_LENGTH;
+}
+
+std::size_t olm::Account::new_account(
+ uint8_t const * random, std::size_t random_length
+) {
+ if (random_length < new_account_random_length()) {
+ last_error = OlmErrorCode::OLM_NOT_ENOUGH_RANDOM;
+ return std::size_t(-1);
+ }
+
+ _olm_crypto_ed25519_generate_key(random, &identity_keys.ed25519_key);
+ random += ED25519_RANDOM_LENGTH;
+ _olm_crypto_curve25519_generate_key(random, &identity_keys.curve25519_key);
+
+ return 0;
+}
+
+namespace {
+
+uint8_t KEY_JSON_ED25519[] = "\"ed25519\":";
+uint8_t KEY_JSON_CURVE25519[] = "\"curve25519\":";
+
+template<typename T>
+static std::uint8_t * write_string(
+ std::uint8_t * pos,
+ T const & value
+) {
+ std::memcpy(pos, value, sizeof(T) - 1);
+ return pos + (sizeof(T) - 1);
+}
+
+}
+
+
+std::size_t olm::Account::get_identity_json_length() const {
+ std::size_t length = 0;
+ length += 1; /* { */
+ length += sizeof(KEY_JSON_CURVE25519) - 1;
+ length += 1; /* " */
+ length += olm::encode_base64_length(
+ sizeof(identity_keys.curve25519_key.public_key)
+ );
+ length += 2; /* ", */
+ length += sizeof(KEY_JSON_ED25519) - 1;
+ length += 1; /* " */
+ length += olm::encode_base64_length(
+ sizeof(identity_keys.ed25519_key.public_key)
+ );
+ length += 2; /* "} */
+ return length;
+}
+
+
+std::size_t olm::Account::get_identity_json(
+ std::uint8_t * identity_json, std::size_t identity_json_length
+) {
+ std::uint8_t * pos = identity_json;
+ size_t expected_length = get_identity_json_length();
+
+ if (identity_json_length < expected_length) {
+ last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
+ return std::size_t(-1);
+ }
+
+ *(pos++) = '{';
+ pos = write_string(pos, KEY_JSON_CURVE25519);
+ *(pos++) = '\"';
+ pos = olm::encode_base64(
+ identity_keys.curve25519_key.public_key.public_key,
+ sizeof(identity_keys.curve25519_key.public_key.public_key),
+ pos
+ );
+ *(pos++) = '\"'; *(pos++) = ',';
+ pos = write_string(pos, KEY_JSON_ED25519);
+ *(pos++) = '\"';
+ pos = olm::encode_base64(
+ identity_keys.ed25519_key.public_key.public_key,
+ sizeof(identity_keys.ed25519_key.public_key.public_key),
+ pos
+ );
+ *(pos++) = '\"'; *(pos++) = '}';
+ return pos - identity_json;
+}
+
+
+std::size_t olm::Account::signature_length(
+) const {
+ return ED25519_SIGNATURE_LENGTH;
+}
+
+
+std::size_t olm::Account::sign(
+ std::uint8_t const * message, std::size_t message_length,
+ std::uint8_t * signature, std::size_t signature_length
+) {
+ if (signature_length < this->signature_length()) {
+ last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
+ return std::size_t(-1);
+ }
+ _olm_crypto_ed25519_sign(
+ &identity_keys.ed25519_key, message, message_length, signature
+ );
+ return this->signature_length();
+}
+
+
+std::size_t olm::Account::get_one_time_keys_json_length(
+) const {
+ std::size_t length = 0;
+ bool is_empty = true;
+ for (auto const & key : one_time_keys) {
+ if (key.published) {
+ continue;
+ }
+ is_empty = false;
+ length += 2; /* {" */
+ length += olm::encode_base64_length(_olm_pickle_uint32_length(key.id));
+ length += 3; /* ":" */
+ length += olm::encode_base64_length(sizeof(key.key.public_key));
+ length += 1; /* " */
+ }
+ if (is_empty) {
+ length += 1; /* { */
+ }
+ length += 3; /* }{} */
+ length += sizeof(KEY_JSON_CURVE25519) - 1;
+ return length;
+}
+
+
+std::size_t olm::Account::get_one_time_keys_json(
+ std::uint8_t * one_time_json, std::size_t one_time_json_length
+) {
+ std::uint8_t * pos = one_time_json;
+ if (one_time_json_length < get_one_time_keys_json_length()) {
+ last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
+ return std::size_t(-1);
+ }
+ *(pos++) = '{';
+ pos = write_string(pos, KEY_JSON_CURVE25519);
+ std::uint8_t sep = '{';
+ for (auto const & key : one_time_keys) {
+ if (key.published) {
+ continue;
+ }
+ *(pos++) = sep;
+ *(pos++) = '\"';
+ std::uint8_t key_id[_olm_pickle_uint32_length(key.id)];
+ _olm_pickle_uint32(key_id, key.id);
+ pos = olm::encode_base64(key_id, sizeof(key_id), pos);
+ *(pos++) = '\"'; *(pos++) = ':'; *(pos++) = '\"';
+ pos = olm::encode_base64(
+ key.key.public_key.public_key, sizeof(key.key.public_key.public_key), pos
+ );
+ *(pos++) = '\"';
+ sep = ',';
+ }
+ if (sep != ',') {
+ /* The list was empty */
+ *(pos++) = sep;
+ }
+ *(pos++) = '}';
+ *(pos++) = '}';
+ return pos - one_time_json;
+}
+
+
+std::size_t olm::Account::mark_keys_as_published(
+) {
+ std::size_t count = 0;
+ for (auto & key : one_time_keys) {
+ if (!key.published) {
+ key.published = true;
+ count++;
+ }
+ }
+ current_fallback_key.published = true;
+ return count;
+}
+
+
+std::size_t olm::Account::max_number_of_one_time_keys(
+) const {
+ return olm::MAX_ONE_TIME_KEYS;
+}
+
+std::size_t olm::Account::generate_one_time_keys_random_length(
+ std::size_t number_of_keys
+) const {
+ return CURVE25519_RANDOM_LENGTH * number_of_keys;
+}
+
+std::size_t olm::Account::generate_one_time_keys(
+ std::size_t number_of_keys,
+ std::uint8_t const * random, std::size_t random_length
+) {
+ if (random_length < generate_one_time_keys_random_length(number_of_keys)) {
+ last_error = OlmErrorCode::OLM_NOT_ENOUGH_RANDOM;
+ return std::size_t(-1);
+ }
+ for (unsigned i = 0; i < number_of_keys; ++i) {
+ OneTimeKey & key = *one_time_keys.insert(one_time_keys.begin());
+ key.id = ++next_one_time_key_id;
+ key.published = false;
+ _olm_crypto_curve25519_generate_key(random, &key.key);
+ random += CURVE25519_RANDOM_LENGTH;
+ }
+ return number_of_keys;
+}
+
+std::size_t olm::Account::generate_fallback_key_random_length() const {
+ return CURVE25519_RANDOM_LENGTH;
+}
+
+std::size_t olm::Account::generate_fallback_key(
+ std::uint8_t const * random, std::size_t random_length
+) {
+ if (random_length < generate_fallback_key_random_length()) {
+ last_error = OlmErrorCode::OLM_NOT_ENOUGH_RANDOM;
+ return std::size_t(-1);
+ }
+ if (num_fallback_keys < 2) {
+ num_fallback_keys++;
+ }
+ prev_fallback_key = current_fallback_key;
+ current_fallback_key.id = ++next_one_time_key_id;
+ current_fallback_key.published = false;
+ _olm_crypto_curve25519_generate_key(random, ¤t_fallback_key.key);
+ return 1;
+}
+
+
+std::size_t olm::Account::get_fallback_key_json_length(
+) const {
+ std::size_t length = 4 + sizeof(KEY_JSON_CURVE25519) - 1; /* {"curve25519":{}} */
+ if (num_fallback_keys >= 1) {
+ const OneTimeKey & key = current_fallback_key;
+ length += 1; /* " */
+ length += olm::encode_base64_length(_olm_pickle_uint32_length(key.id));
+ length += 3; /* ":" */
+ length += olm::encode_base64_length(sizeof(key.key.public_key));
+ length += 1; /* " */
+ }
+ return length;
+}
+
+std::size_t olm::Account::get_fallback_key_json(
+ std::uint8_t * fallback_json, std::size_t fallback_json_length
+) {
+ std::uint8_t * pos = fallback_json;
+ if (fallback_json_length < get_fallback_key_json_length()) {
+ last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
+ return std::size_t(-1);
+ }
+ *(pos++) = '{';
+ pos = write_string(pos, KEY_JSON_CURVE25519);
+ *(pos++) = '{';
+ OneTimeKey & key = current_fallback_key;
+ if (num_fallback_keys >= 1) {
+ *(pos++) = '\"';
+ std::uint8_t key_id[_olm_pickle_uint32_length(key.id)];
+ _olm_pickle_uint32(key_id, key.id);
+ pos = olm::encode_base64(key_id, sizeof(key_id), pos);
+ *(pos++) = '\"'; *(pos++) = ':'; *(pos++) = '\"';
+ pos = olm::encode_base64(
+ key.key.public_key.public_key, sizeof(key.key.public_key.public_key), pos
+ );
+ *(pos++) = '\"';
+ }
+ *(pos++) = '}';
+ *(pos++) = '}';
+ return pos - fallback_json;
+}
+
+std::size_t olm::Account::get_unpublished_fallback_key_json_length(
+) const {
+ std::size_t length = 4 + sizeof(KEY_JSON_CURVE25519) - 1; /* {"curve25519":{}} */
+ const OneTimeKey & key = current_fallback_key;
+ if (num_fallback_keys >= 1 && !key.published) {
+ length += 1; /* " */
+ length += olm::encode_base64_length(_olm_pickle_uint32_length(key.id));
+ length += 3; /* ":" */
+ length += olm::encode_base64_length(sizeof(key.key.public_key));
+ length += 1; /* " */
+ }
+ return length;
+}
+
+std::size_t olm::Account::get_unpublished_fallback_key_json(
+ std::uint8_t * fallback_json, std::size_t fallback_json_length
+) {
+ std::uint8_t * pos = fallback_json;
+ if (fallback_json_length < get_unpublished_fallback_key_json_length()) {
+ last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
+ return std::size_t(-1);
+ }
+ *(pos++) = '{';
+ pos = write_string(pos, KEY_JSON_CURVE25519);
+ *(pos++) = '{';
+ OneTimeKey & key = current_fallback_key;
+ if (num_fallback_keys >= 1 && !key.published) {
+ *(pos++) = '\"';
+ std::uint8_t key_id[_olm_pickle_uint32_length(key.id)];
+ _olm_pickle_uint32(key_id, key.id);
+ pos = olm::encode_base64(key_id, sizeof(key_id), pos);
+ *(pos++) = '\"'; *(pos++) = ':'; *(pos++) = '\"';
+ pos = olm::encode_base64(
+ key.key.public_key.public_key, sizeof(key.key.public_key.public_key), pos
+ );
+ *(pos++) = '\"';
+ }
+ *(pos++) = '}';
+ *(pos++) = '}';
+ return pos - fallback_json;
+}
+
+void olm::Account::forget_old_fallback_key(
+) {
+ if (num_fallback_keys >= 2) {
+ num_fallback_keys = 1;
+ olm::unset(&prev_fallback_key, sizeof(prev_fallback_key));
+ }
+}
+
+namespace olm {
+
+static std::size_t pickle_length(
+ olm::IdentityKeys const & value
+) {
+ size_t length = 0;
+ length += _olm_pickle_ed25519_key_pair_length(&value.ed25519_key);
+ length += olm::pickle_length(value.curve25519_key);
+ return length;
+}
+
+
+static std::uint8_t * pickle(
+ std::uint8_t * pos,
+ olm::IdentityKeys const & value
+) {
+ pos = _olm_pickle_ed25519_key_pair(pos, &value.ed25519_key);
+ pos = olm::pickle(pos, value.curve25519_key);
+ return pos;
+}
+
+
+static std::uint8_t const * unpickle(
+ std::uint8_t const * pos, std::uint8_t const * end,
+ olm::IdentityKeys & value
+) {
+ pos = _olm_unpickle_ed25519_key_pair(pos, end, &value.ed25519_key); UNPICKLE_OK(pos);
+ pos = olm::unpickle(pos, end, value.curve25519_key); UNPICKLE_OK(pos);
+ return pos;
+}
+
+
+static std::size_t pickle_length(
+ olm::OneTimeKey const & value
+) {
+ std::size_t length = 0;
+ length += olm::pickle_length(value.id);
+ length += olm::pickle_length(value.published);
+ length += olm::pickle_length(value.key);
+ return length;
+}
+
+
+static std::uint8_t * pickle(
+ std::uint8_t * pos,
+ olm::OneTimeKey const & value
+) {
+ pos = olm::pickle(pos, value.id);
+ pos = olm::pickle(pos, value.published);
+ pos = olm::pickle(pos, value.key);
+ return pos;
+}
+
+
+static std::uint8_t const * unpickle(
+ std::uint8_t const * pos, std::uint8_t const * end,
+ olm::OneTimeKey & value
+) {
+ pos = olm::unpickle(pos, end, value.id); UNPICKLE_OK(pos);
+ pos = olm::unpickle(pos, end, value.published); UNPICKLE_OK(pos);
+ pos = olm::unpickle(pos, end, value.key); UNPICKLE_OK(pos);
+ return pos;
+}
+
+} // namespace olm
+
+namespace {
+// pickle version 1 used only 32 bytes for the ed25519 private key.
+// Any keys thus used should be considered compromised.
+// pickle version 2 does not have fallback keys.
+// pickle version 3 does not store whether the current fallback key is published.
+static const std::uint32_t ACCOUNT_PICKLE_VERSION = 4;
+}
+
+
+std::size_t olm::pickle_length(
+ olm::Account const & value
+) {
+ std::size_t length = 0;
+ length += olm::pickle_length(ACCOUNT_PICKLE_VERSION);
+ length += olm::pickle_length(value.identity_keys);
+ length += olm::pickle_length(value.one_time_keys);
+ length += olm::pickle_length(value.num_fallback_keys);
+ if (value.num_fallback_keys >= 1) {
+ length += olm::pickle_length(value.current_fallback_key);
+ if (value.num_fallback_keys >= 2) {
+ length += olm::pickle_length(value.prev_fallback_key);
+ }
+ }
+ length += olm::pickle_length(value.next_one_time_key_id);
+ return length;
+}
+
+
+std::uint8_t * olm::pickle(
+ std::uint8_t * pos,
+ olm::Account const & value
+) {
+ pos = olm::pickle(pos, ACCOUNT_PICKLE_VERSION);
+ pos = olm::pickle(pos, value.identity_keys);
+ pos = olm::pickle(pos, value.one_time_keys);
+ pos = olm::pickle(pos, value.num_fallback_keys);
+ if (value.num_fallback_keys >= 1) {
+ pos = olm::pickle(pos, value.current_fallback_key);
+ if (value.num_fallback_keys >= 2) {
+ pos = olm::pickle(pos, value.prev_fallback_key);
+ }
+ }
+ pos = olm::pickle(pos, value.next_one_time_key_id);
+ return pos;
+}
+
+
+std::uint8_t const * olm::unpickle(
+ std::uint8_t const * pos, std::uint8_t const * end,
+ olm::Account & value
+) {
+ uint32_t pickle_version;
+
+ pos = olm::unpickle(pos, end, pickle_version); UNPICKLE_OK(pos);
+
+ switch (pickle_version) {
+ case ACCOUNT_PICKLE_VERSION:
+ case 3:
+ case 2:
+ break;
+ case 1:
+ value.last_error = OlmErrorCode::OLM_BAD_LEGACY_ACCOUNT_PICKLE;
+ return nullptr;
+ default:
+ value.last_error = OlmErrorCode::OLM_UNKNOWN_PICKLE_VERSION;
+ return nullptr;
+ }
+
+ pos = olm::unpickle(pos, end, value.identity_keys); UNPICKLE_OK(pos);
+ pos = olm::unpickle(pos, end, value.one_time_keys); UNPICKLE_OK(pos);
+
+ if (pickle_version <= 2) {
+ // version 2 did not have fallback keys
+ value.num_fallback_keys = 0;
+ } else if (pickle_version == 3) {
+ // version 3 used the published flag to indicate how many fallback keys
+ // were present (we'll have to assume that the keys were published)
+ pos = olm::unpickle(pos, end, value.current_fallback_key); UNPICKLE_OK(pos);
+ pos = olm::unpickle(pos, end, value.prev_fallback_key); UNPICKLE_OK(pos);
+ if (value.current_fallback_key.published) {
+ if (value.prev_fallback_key.published) {
+ value.num_fallback_keys = 2;
+ } else {
+ value.num_fallback_keys = 1;
+ }
+ } else {
+ value.num_fallback_keys = 0;
+ }
+ } else {
+ pos = olm::unpickle(pos, end, value.num_fallback_keys); UNPICKLE_OK(pos);
+ if (value.num_fallback_keys >= 1) {
+ pos = olm::unpickle(pos, end, value.current_fallback_key); UNPICKLE_OK(pos);
+ if (value.num_fallback_keys >= 2) {
+ pos = olm::unpickle(pos, end, value.prev_fallback_key); UNPICKLE_OK(pos);
+ if (value.num_fallback_keys >= 3) {
+ value.last_error = OlmErrorCode::OLM_CORRUPTED_PICKLE;
+ return nullptr;
+ }
+ }
+ }
+ }
+
+ pos = olm::unpickle(pos, end, value.next_one_time_key_id); UNPICKLE_OK(pos);
+
+ return pos;
+}
--- /dev/null
+/* Copyright 2015 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <cassert>
+
+#include "olm/base64.h"
+#include "olm/base64.hh"
+
+namespace {
+
+static const std::uint8_t ENCODE_BASE64[64] = {
+ 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
+ 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
+ 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
+ 0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
+ 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E,
+ 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76,
+ 0x77, 0x78, 0x79, 0x7A, 0x30, 0x31, 0x32, 0x33,
+ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2B, 0x2F,
+};
+
+static const std::uint8_t E = -1;
+
+static const std::uint8_t DECODE_BASE64[128] = {
+/* 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xA 0xB 0xC 0xD 0xE 0xF */
+ E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E,
+ E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E,
+ E, E, E, E, E, E, E, E, E, E, E, 62, E, E, E, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, E, E, E, E, E, E,
+ E, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, E, E, E, E, E,
+ E, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, E, E, E, E, E,
+};
+
+} // namespace
+
+
+std::size_t olm::encode_base64_length(
+ std::size_t input_length
+) {
+ return 4 * ((input_length + 2) / 3) + (input_length + 2) % 3 - 2;
+}
+
+std::uint8_t * olm::encode_base64(
+ std::uint8_t const * input, std::size_t input_length,
+ std::uint8_t * output
+) {
+ std::uint8_t const * end = input + (input_length / 3) * 3;
+ std::uint8_t const * pos = input;
+ while (pos != end) {
+ unsigned value = pos[0];
+ value <<= 8; value |= pos[1];
+ value <<= 8; value |= pos[2];
+ pos += 3;
+ output[3] = ENCODE_BASE64[value & 0x3F];
+ value >>= 6; output[2] = ENCODE_BASE64[value & 0x3F];
+ value >>= 6; output[1] = ENCODE_BASE64[value & 0x3F];
+ value >>= 6; output[0] = ENCODE_BASE64[value];
+ output += 4;
+ }
+ unsigned remainder = input + input_length - pos;
+ std::uint8_t * result = output;
+ if (remainder) {
+ unsigned value = pos[0];
+ if (remainder == 2) {
+ value <<= 8; value |= pos[1];
+ value <<= 2;
+ output[2] = ENCODE_BASE64[value & 0x3F];
+ value >>= 6;
+ result += 3;
+ } else {
+ value <<= 4;
+ result += 2;
+ }
+ output[1] = ENCODE_BASE64[value & 0x3F];
+ value >>= 6;
+ output[0] = ENCODE_BASE64[value];
+ }
+ return result;
+}
+
+
+std::size_t olm::decode_base64_length(
+ std::size_t input_length
+) {
+ if (input_length % 4 == 1) {
+ return std::size_t(-1);
+ } else {
+ return 3 * ((input_length + 2) / 4) + (input_length + 2) % 4 - 2;
+ }
+}
+
+
+std::size_t olm::decode_base64(
+ std::uint8_t const * input, std::size_t input_length,
+ std::uint8_t * output
+) {
+ size_t raw_length = olm::decode_base64_length(input_length);
+
+ if (raw_length == std::size_t(-1)) {
+ return std::size_t(-1);
+ }
+
+ std::uint8_t const * end = input + (input_length / 4) * 4;
+ std::uint8_t const * pos = input;
+
+ while (pos != end) {
+ unsigned value = DECODE_BASE64[pos[0] & 0x7F];
+ value <<= 6; value |= DECODE_BASE64[pos[1] & 0x7F];
+ value <<= 6; value |= DECODE_BASE64[pos[2] & 0x7F];
+ value <<= 6; value |= DECODE_BASE64[pos[3] & 0x7F];
+ pos += 4;
+ output[2] = value;
+ value >>= 8; output[1] = value;
+ value >>= 8; output[0] = value;
+ output += 3;
+ }
+
+ unsigned remainder = input + input_length - pos;
+ if (remainder) {
+ /* A base64 payload with a single byte remainder cannot occur because
+ * a single base64 character only encodes 6 bits, which is less than
+ * a full byte. Therefore, a minimum of two base64 characters are
+ * required to construct a single output byte and payloads with
+ * a remainder of 1 are illegal.
+ *
+ * Should never be the case due to length check above.
+ */
+ assert(remainder != 1);
+
+ unsigned value = DECODE_BASE64[pos[0] & 0x7F];
+ value <<= 6; value |= DECODE_BASE64[pos[1] & 0x7F];
+ if (remainder == 3) {
+ value <<= 6; value |= DECODE_BASE64[pos[2] & 0x7F];
+ value >>= 2;
+ output[1] = value;
+ value >>= 8;
+ } else {
+ value >>= 4;
+ }
+ output[0] = value;
+ }
+
+ return raw_length;
+}
+
+
+// implementations of base64.h
+
+size_t _olm_encode_base64_length(
+ size_t input_length
+) {
+ return olm::encode_base64_length(input_length);
+}
+
+size_t _olm_encode_base64(
+ uint8_t const * input, size_t input_length,
+ uint8_t * output
+) {
+ uint8_t * r = olm::encode_base64(input, input_length, output);
+ return r - output;
+}
+
+size_t _olm_decode_base64_length(
+ size_t input_length
+) {
+ return olm::decode_base64_length(input_length);
+}
+
+size_t _olm_decode_base64(
+ uint8_t const * input, size_t input_length,
+ uint8_t * output
+) {
+ return olm::decode_base64(input, input_length, output);
+}
--- /dev/null
+/* Copyright 2015 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "olm/cipher.h"
+#include "olm/crypto.h"
+#include "olm/memory.hh"
+#include <cstring>
+
+const std::size_t HMAC_KEY_LENGTH = 32;
+
+namespace {
+
+struct DerivedKeys {
+ _olm_aes256_key aes_key;
+ std::uint8_t mac_key[HMAC_KEY_LENGTH];
+ _olm_aes256_iv aes_iv;
+};
+
+
+static void derive_keys(
+ std::uint8_t const * kdf_info, std::size_t kdf_info_length,
+ std::uint8_t const * key, std::size_t key_length,
+ DerivedKeys & keys
+) {
+ std::uint8_t derived_secrets[
+ AES256_KEY_LENGTH + HMAC_KEY_LENGTH + AES256_IV_LENGTH
+ ];
+ _olm_crypto_hkdf_sha256(
+ key, key_length,
+ nullptr, 0,
+ kdf_info, kdf_info_length,
+ derived_secrets, sizeof(derived_secrets)
+ );
+ std::uint8_t const * pos = derived_secrets;
+ pos = olm::load_array(keys.aes_key.key, pos);
+ pos = olm::load_array(keys.mac_key, pos);
+ pos = olm::load_array(keys.aes_iv.iv, pos);
+ olm::unset(derived_secrets);
+}
+
+static const std::size_t MAC_LENGTH = 8;
+
+size_t aes_sha_256_cipher_mac_length(const struct _olm_cipher *cipher) {
+ return MAC_LENGTH;
+}
+
+size_t aes_sha_256_cipher_encrypt_ciphertext_length(
+ const struct _olm_cipher *cipher, size_t plaintext_length
+) {
+ return _olm_crypto_aes_encrypt_cbc_length(plaintext_length);
+}
+
+size_t aes_sha_256_cipher_encrypt(
+ const struct _olm_cipher *cipher,
+ uint8_t const * key, size_t key_length,
+ uint8_t const * plaintext, size_t plaintext_length,
+ uint8_t * ciphertext, size_t ciphertext_length,
+ uint8_t * output, size_t output_length
+) {
+ auto *c = reinterpret_cast<const _olm_cipher_aes_sha_256 *>(cipher);
+
+ if (ciphertext_length
+ < aes_sha_256_cipher_encrypt_ciphertext_length(cipher, plaintext_length)
+ || output_length < MAC_LENGTH) {
+ return std::size_t(-1);
+ }
+
+ struct DerivedKeys keys;
+ std::uint8_t mac[SHA256_OUTPUT_LENGTH];
+
+ derive_keys(c->kdf_info, c->kdf_info_length, key, key_length, keys);
+
+ _olm_crypto_aes_encrypt_cbc(
+ &keys.aes_key, &keys.aes_iv, plaintext, plaintext_length, ciphertext
+ );
+
+ _olm_crypto_hmac_sha256(
+ keys.mac_key, HMAC_KEY_LENGTH, output, output_length - MAC_LENGTH, mac
+ );
+
+ std::memcpy(output + output_length - MAC_LENGTH, mac, MAC_LENGTH);
+
+ olm::unset(keys);
+ return output_length;
+}
+
+
+size_t aes_sha_256_cipher_decrypt_max_plaintext_length(
+ const struct _olm_cipher *cipher,
+ size_t ciphertext_length
+) {
+ return ciphertext_length;
+}
+
+size_t aes_sha_256_cipher_decrypt(
+ const struct _olm_cipher *cipher,
+ uint8_t const * key, size_t key_length,
+ uint8_t const * input, size_t input_length,
+ uint8_t const * ciphertext, size_t ciphertext_length,
+ uint8_t * plaintext, size_t max_plaintext_length
+) {
+ if (max_plaintext_length
+ < aes_sha_256_cipher_decrypt_max_plaintext_length(cipher, ciphertext_length)
+ || input_length < MAC_LENGTH) {
+ return std::size_t(-1);
+ }
+
+ auto *c = reinterpret_cast<const _olm_cipher_aes_sha_256 *>(cipher);
+
+ DerivedKeys keys;
+ std::uint8_t mac[SHA256_OUTPUT_LENGTH];
+
+ derive_keys(c->kdf_info, c->kdf_info_length, key, key_length, keys);
+
+ _olm_crypto_hmac_sha256(
+ keys.mac_key, HMAC_KEY_LENGTH, input, input_length - MAC_LENGTH, mac
+ );
+
+ std::uint8_t const * input_mac = input + input_length - MAC_LENGTH;
+ if (!olm::is_equal(input_mac, mac, MAC_LENGTH)) {
+ olm::unset(keys);
+ return std::size_t(-1);
+ }
+
+ std::size_t plaintext_length = _olm_crypto_aes_decrypt_cbc(
+ &keys.aes_key, &keys.aes_iv, ciphertext, ciphertext_length, plaintext
+ );
+
+ olm::unset(keys);
+ return plaintext_length;
+}
+
+} // namespace
+
+const struct _olm_cipher_ops _olm_cipher_aes_sha_256_ops = {
+ aes_sha_256_cipher_mac_length,
+ aes_sha_256_cipher_encrypt_ciphertext_length,
+ aes_sha_256_cipher_encrypt,
+ aes_sha_256_cipher_decrypt_max_plaintext_length,
+ aes_sha_256_cipher_decrypt,
+};
--- /dev/null
+/* Copyright 2015 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "olm/crypto.h"
+#include "olm/memory.hh"
+
+#include <cstring>
+
+extern "C" {
+
+#include "crypto-algorithms/aes.h"
+#include "crypto-algorithms/sha256.h"
+
+}
+
+#include "ed25519/src/ed25519.h"
+#include "curve25519-donna.h"
+
+namespace {
+
+static const std::uint8_t CURVE25519_BASEPOINT[32] = {9};
+static const std::size_t AES_KEY_SCHEDULE_LENGTH = 60;
+static const std::size_t AES_KEY_BITS = 8 * AES256_KEY_LENGTH;
+static const std::size_t AES_BLOCK_LENGTH = 16;
+static const std::size_t SHA256_BLOCK_LENGTH = 64;
+static const std::uint8_t HKDF_DEFAULT_SALT[32] = {};
+
+
+template<std::size_t block_size>
+inline static void xor_block(
+ std::uint8_t * block,
+ std::uint8_t const * input
+) {
+ for (std::size_t i = 0; i < block_size; ++i) {
+ block[i] ^= input[i];
+ }
+}
+
+
+inline static void hmac_sha256_key(
+ std::uint8_t const * input_key, std::size_t input_key_length,
+ std::uint8_t * hmac_key
+) {
+ std::memset(hmac_key, 0, SHA256_BLOCK_LENGTH);
+ if (input_key_length > SHA256_BLOCK_LENGTH) {
+ ::SHA256_CTX context;
+ ::sha256_init(&context);
+ ::sha256_update(&context, input_key, input_key_length);
+ ::sha256_final(&context, hmac_key);
+ } else {
+ std::memcpy(hmac_key, input_key, input_key_length);
+ }
+}
+
+
+inline static void hmac_sha256_init(
+ ::SHA256_CTX * context,
+ std::uint8_t const * hmac_key
+) {
+ std::uint8_t i_pad[SHA256_BLOCK_LENGTH];
+ std::memcpy(i_pad, hmac_key, SHA256_BLOCK_LENGTH);
+ for (std::size_t i = 0; i < SHA256_BLOCK_LENGTH; ++i) {
+ i_pad[i] ^= 0x36;
+ }
+ ::sha256_init(context);
+ ::sha256_update(context, i_pad, SHA256_BLOCK_LENGTH);
+ olm::unset(i_pad);
+}
+
+
+inline static void hmac_sha256_final(
+ ::SHA256_CTX * context,
+ std::uint8_t const * hmac_key,
+ std::uint8_t * output
+) {
+ std::uint8_t o_pad[SHA256_BLOCK_LENGTH + SHA256_OUTPUT_LENGTH];
+ std::memcpy(o_pad, hmac_key, SHA256_BLOCK_LENGTH);
+ for (std::size_t i = 0; i < SHA256_BLOCK_LENGTH; ++i) {
+ o_pad[i] ^= 0x5C;
+ }
+ ::sha256_final(context, o_pad + SHA256_BLOCK_LENGTH);
+ ::SHA256_CTX final_context;
+ ::sha256_init(&final_context);
+ ::sha256_update(&final_context, o_pad, sizeof(o_pad));
+ ::sha256_final(&final_context, output);
+ olm::unset(final_context);
+ olm::unset(o_pad);
+}
+
+} // namespace
+
+void _olm_crypto_curve25519_generate_key(
+ uint8_t const * random_32_bytes,
+ struct _olm_curve25519_key_pair *key_pair
+) {
+ std::memcpy(
+ key_pair->private_key.private_key, random_32_bytes,
+ CURVE25519_KEY_LENGTH
+ );
+ ::curve25519_donna(
+ key_pair->public_key.public_key,
+ key_pair->private_key.private_key,
+ CURVE25519_BASEPOINT
+ );
+}
+
+
+void _olm_crypto_curve25519_shared_secret(
+ const struct _olm_curve25519_key_pair *our_key,
+ const struct _olm_curve25519_public_key * their_key,
+ std::uint8_t * output
+) {
+ ::curve25519_donna(output, our_key->private_key.private_key, their_key->public_key);
+}
+
+
+void _olm_crypto_ed25519_generate_key(
+ std::uint8_t const * random_32_bytes,
+ struct _olm_ed25519_key_pair *key_pair
+) {
+ ::ed25519_create_keypair(
+ key_pair->public_key.public_key, key_pair->private_key.private_key,
+ random_32_bytes
+ );
+}
+
+
+void _olm_crypto_ed25519_sign(
+ const struct _olm_ed25519_key_pair *our_key,
+ std::uint8_t const * message, std::size_t message_length,
+ std::uint8_t * output
+) {
+ ::ed25519_sign(
+ output,
+ message, message_length,
+ our_key->public_key.public_key,
+ our_key->private_key.private_key
+ );
+}
+
+
+int _olm_crypto_ed25519_verify(
+ const struct _olm_ed25519_public_key *their_key,
+ std::uint8_t const * message, std::size_t message_length,
+ std::uint8_t const * signature
+) {
+ return 0 != ::ed25519_verify(
+ signature,
+ message, message_length,
+ their_key->public_key
+ );
+}
+
+
+std::size_t _olm_crypto_aes_encrypt_cbc_length(
+ std::size_t input_length
+) {
+ return input_length + AES_BLOCK_LENGTH - input_length % AES_BLOCK_LENGTH;
+}
+
+
+void _olm_crypto_aes_encrypt_cbc(
+ _olm_aes256_key const *key,
+ _olm_aes256_iv const *iv,
+ std::uint8_t const * input, std::size_t input_length,
+ std::uint8_t * output
+) {
+ std::uint32_t key_schedule[AES_KEY_SCHEDULE_LENGTH];
+ ::_olm_aes_key_setup(key->key, key_schedule, AES_KEY_BITS);
+ std::uint8_t input_block[AES_BLOCK_LENGTH];
+ std::memcpy(input_block, iv->iv, AES_BLOCK_LENGTH);
+ while (input_length >= AES_BLOCK_LENGTH) {
+ xor_block<AES_BLOCK_LENGTH>(input_block, input);
+ ::_olm_aes_encrypt(input_block, output, key_schedule, AES_KEY_BITS);
+ std::memcpy(input_block, output, AES_BLOCK_LENGTH);
+ input += AES_BLOCK_LENGTH;
+ output += AES_BLOCK_LENGTH;
+ input_length -= AES_BLOCK_LENGTH;
+ }
+ std::size_t i = 0;
+ for (; i < input_length; ++i) {
+ input_block[i] ^= input[i];
+ }
+ for (; i < AES_BLOCK_LENGTH; ++i) {
+ input_block[i] ^= AES_BLOCK_LENGTH - input_length;
+ }
+ ::_olm_aes_encrypt(input_block, output, key_schedule, AES_KEY_BITS);
+ olm::unset(key_schedule);
+ olm::unset(input_block);
+}
+
+
+std::size_t _olm_crypto_aes_decrypt_cbc(
+ _olm_aes256_key const *key,
+ _olm_aes256_iv const *iv,
+ std::uint8_t const * input, std::size_t input_length,
+ std::uint8_t * output
+) {
+ std::uint32_t key_schedule[AES_KEY_SCHEDULE_LENGTH];
+ ::_olm_aes_key_setup(key->key, key_schedule, AES_KEY_BITS);
+ std::uint8_t block1[AES_BLOCK_LENGTH];
+ std::uint8_t block2[AES_BLOCK_LENGTH];
+ std::memcpy(block1, iv->iv, AES_BLOCK_LENGTH);
+ for (std::size_t i = 0; i < input_length; i += AES_BLOCK_LENGTH) {
+ std::memcpy(block2, &input[i], AES_BLOCK_LENGTH);
+ ::_olm_aes_decrypt(&input[i], &output[i], key_schedule, AES_KEY_BITS);
+ xor_block<AES_BLOCK_LENGTH>(&output[i], block1);
+ std::memcpy(block1, block2, AES_BLOCK_LENGTH);
+ }
+ olm::unset(key_schedule);
+ olm::unset(block1);
+ olm::unset(block2);
+ std::size_t padding = output[input_length - 1];
+ return (padding > input_length) ? std::size_t(-1) : (input_length - padding);
+}
+
+
+void _olm_crypto_sha256(
+ std::uint8_t const * input, std::size_t input_length,
+ std::uint8_t * output
+) {
+ ::SHA256_CTX context;
+ ::sha256_init(&context);
+ ::sha256_update(&context, input, input_length);
+ ::sha256_final(&context, output);
+ olm::unset(context);
+}
+
+
+void _olm_crypto_hmac_sha256(
+ std::uint8_t const * key, std::size_t key_length,
+ std::uint8_t const * input, std::size_t input_length,
+ std::uint8_t * output
+) {
+ std::uint8_t hmac_key[SHA256_BLOCK_LENGTH];
+ ::SHA256_CTX context;
+ hmac_sha256_key(key, key_length, hmac_key);
+ hmac_sha256_init(&context, hmac_key);
+ ::sha256_update(&context, input, input_length);
+ hmac_sha256_final(&context, hmac_key, output);
+ olm::unset(hmac_key);
+ olm::unset(context);
+}
+
+
+void _olm_crypto_hkdf_sha256(
+ std::uint8_t const * input, std::size_t input_length,
+ std::uint8_t const * salt, std::size_t salt_length,
+ std::uint8_t const * info, std::size_t info_length,
+ std::uint8_t * output, std::size_t output_length
+) {
+ ::SHA256_CTX context;
+ std::uint8_t hmac_key[SHA256_BLOCK_LENGTH];
+ std::uint8_t step_result[SHA256_OUTPUT_LENGTH];
+ std::size_t bytes_remaining = output_length;
+ std::uint8_t iteration = 1;
+ if (!salt) {
+ salt = HKDF_DEFAULT_SALT;
+ salt_length = sizeof(HKDF_DEFAULT_SALT);
+ }
+ /* Extract */
+ hmac_sha256_key(salt, salt_length, hmac_key);
+ hmac_sha256_init(&context, hmac_key);
+ ::sha256_update(&context, input, input_length);
+ hmac_sha256_final(&context, hmac_key, step_result);
+ hmac_sha256_key(step_result, SHA256_OUTPUT_LENGTH, hmac_key);
+
+ /* Expand */
+ hmac_sha256_init(&context, hmac_key);
+ ::sha256_update(&context, info, info_length);
+ ::sha256_update(&context, &iteration, 1);
+ hmac_sha256_final(&context, hmac_key, step_result);
+ while (bytes_remaining > SHA256_OUTPUT_LENGTH) {
+ std::memcpy(output, step_result, SHA256_OUTPUT_LENGTH);
+ output += SHA256_OUTPUT_LENGTH;
+ bytes_remaining -= SHA256_OUTPUT_LENGTH;
+ iteration ++;
+ hmac_sha256_init(&context, hmac_key);
+ ::sha256_update(&context, step_result, SHA256_OUTPUT_LENGTH);
+ ::sha256_update(&context, info, info_length);
+ ::sha256_update(&context, &iteration, 1);
+ hmac_sha256_final(&context, hmac_key, step_result);
+ }
+ std::memcpy(output, step_result, bytes_remaining);
+ olm::unset(context);
+ olm::unset(hmac_key);
+ olm::unset(step_result);
+}
--- /dev/null
+/* Copyright 2015-6 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define select ed25519_select
+#include "ed25519/src/fe.c"
+#include "ed25519/src/sc.c"
+#include "ed25519/src/ge.c"
+#include "ed25519/src/keypair.c"
+#include "ed25519/src/sha512.c"
+#include "ed25519/src/verify.c"
+#include "ed25519/src/sign.c"
--- /dev/null
+/* Copyright 2016 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "olm/error.h"
+
+static const char * ERRORS[] = {
+ "SUCCESS",
+ "NOT_ENOUGH_RANDOM",
+ "OUTPUT_BUFFER_TOO_SMALL",
+ "BAD_MESSAGE_VERSION",
+ "BAD_MESSAGE_FORMAT",
+ "BAD_MESSAGE_MAC",
+ "BAD_MESSAGE_KEY_ID",
+ "INVALID_BASE64",
+ "BAD_ACCOUNT_KEY",
+ "UNKNOWN_PICKLE_VERSION",
+ "CORRUPTED_PICKLE",
+ "BAD_SESSION_KEY",
+ "UNKNOWN_MESSAGE_INDEX",
+ "BAD_LEGACY_ACCOUNT_PICKLE",
+ "BAD_SIGNATURE",
+ "OLM_INPUT_BUFFER_TOO_SMALL",
+ "OLM_SAS_THEIR_KEY_NOT_SET",
+ "OLM_PICKLE_EXTRA_DATA"
+};
+
+const char * _olm_error_to_string(enum OlmErrorCode error)
+{
+ if (error < (sizeof(ERRORS)/sizeof(ERRORS[0]))) {
+ return ERRORS[error];
+ } else {
+ return "UNKNOWN_ERROR";
+ }
+}
--- /dev/null
+/* Copyright 2016 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "olm/inbound_group_session.h"
+
+#include <string.h>
+
+#include "olm/base64.h"
+#include "olm/cipher.h"
+#include "olm/crypto.h"
+#include "olm/error.h"
+#include "olm/megolm.h"
+#include "olm/memory.h"
+#include "olm/message.h"
+#include "olm/pickle.h"
+#include "olm/pickle_encoding.h"
+
+
+#define OLM_PROTOCOL_VERSION 3
+#define GROUP_SESSION_ID_LENGTH ED25519_PUBLIC_KEY_LENGTH
+#define PICKLE_VERSION 2
+#define SESSION_KEY_VERSION 2
+#define SESSION_EXPORT_VERSION 1
+
+struct OlmInboundGroupSession {
+ /** our earliest known ratchet value */
+ Megolm initial_ratchet;
+
+ /** The most recent ratchet value */
+ Megolm latest_ratchet;
+
+ /** The ed25519 signing key */
+ struct _olm_ed25519_public_key signing_key;
+
+ /**
+ * Have we ever seen any evidence that this is a valid session?
+ * (either because the original session share was signed, or because we
+ * have subsequently successfully decrypted a message)
+ *
+ * (We don't do anything with this currently, but we may want to bear it in
+ * mind when we consider handling key-shares for sessions we already know
+ * about.)
+ */
+ int signing_key_verified;
+
+ enum OlmErrorCode last_error;
+};
+
+size_t olm_inbound_group_session_size(void) {
+ return sizeof(OlmInboundGroupSession);
+}
+
+OlmInboundGroupSession * olm_inbound_group_session(
+ void *memory
+) {
+ OlmInboundGroupSession *session = memory;
+ olm_clear_inbound_group_session(session);
+ return session;
+}
+
+const char *olm_inbound_group_session_last_error(
+ const OlmInboundGroupSession *session
+) {
+ return _olm_error_to_string(session->last_error);
+}
+
+enum OlmErrorCode olm_inbound_group_session_last_error_code(
+ const OlmInboundGroupSession *session
+) {
+ return session->last_error;
+}
+
+size_t olm_clear_inbound_group_session(
+ OlmInboundGroupSession *session
+) {
+ _olm_unset(session, sizeof(OlmInboundGroupSession));
+ return sizeof(OlmInboundGroupSession);
+}
+
+#define SESSION_EXPORT_RAW_LENGTH \
+ (1 + 4 + MEGOLM_RATCHET_LENGTH + ED25519_PUBLIC_KEY_LENGTH)
+
+#define SESSION_KEY_RAW_LENGTH \
+ (1 + 4 + MEGOLM_RATCHET_LENGTH + ED25519_PUBLIC_KEY_LENGTH\
+ + ED25519_SIGNATURE_LENGTH)
+
+static size_t _init_group_session_keys(
+ OlmInboundGroupSession *session,
+ const uint8_t *key_buf,
+ int export_format
+) {
+ const uint8_t expected_version =
+ (export_format ? SESSION_EXPORT_VERSION : SESSION_KEY_VERSION);
+ const uint8_t *ptr = key_buf;
+ size_t version = *ptr++;
+
+ if (version != expected_version) {
+ session->last_error = OLM_BAD_SESSION_KEY;
+ return (size_t)-1;
+ }
+
+ uint32_t counter = 0;
+ // Decode counter as a big endian 32-bit number.
+ for (unsigned i = 0; i < 4; i++) {
+ counter <<= 8; counter |= *ptr++;
+ }
+
+ megolm_init(&session->initial_ratchet, ptr, counter);
+ megolm_init(&session->latest_ratchet, ptr, counter);
+
+ ptr += MEGOLM_RATCHET_LENGTH;
+ memcpy(
+ session->signing_key.public_key, ptr, ED25519_PUBLIC_KEY_LENGTH
+ );
+ ptr += ED25519_PUBLIC_KEY_LENGTH;
+
+ if (!export_format) {
+ if (!_olm_crypto_ed25519_verify(&session->signing_key, key_buf,
+ ptr - key_buf, ptr)) {
+ session->last_error = OLM_BAD_SIGNATURE;
+ return (size_t)-1;
+ }
+
+ /* signed keyshare */
+ session->signing_key_verified = 1;
+ }
+ return 0;
+}
+
+size_t olm_init_inbound_group_session(
+ OlmInboundGroupSession *session,
+ const uint8_t * session_key, size_t session_key_length
+) {
+ uint8_t key_buf[SESSION_KEY_RAW_LENGTH];
+ size_t raw_length = _olm_decode_base64_length(session_key_length);
+ size_t result;
+
+ if (raw_length == (size_t)-1) {
+ session->last_error = OLM_INVALID_BASE64;
+ return (size_t)-1;
+ }
+
+ if (raw_length != SESSION_KEY_RAW_LENGTH) {
+ session->last_error = OLM_BAD_SESSION_KEY;
+ return (size_t)-1;
+ }
+
+ _olm_decode_base64(session_key, session_key_length, key_buf);
+ result = _init_group_session_keys(session, key_buf, 0);
+ _olm_unset(key_buf, SESSION_KEY_RAW_LENGTH);
+ return result;
+}
+
+size_t olm_import_inbound_group_session(
+ OlmInboundGroupSession *session,
+ const uint8_t * session_key, size_t session_key_length
+) {
+ uint8_t key_buf[SESSION_EXPORT_RAW_LENGTH];
+ size_t raw_length = _olm_decode_base64_length(session_key_length);
+ size_t result;
+
+ if (raw_length == (size_t)-1) {
+ session->last_error = OLM_INVALID_BASE64;
+ return (size_t)-1;
+ }
+
+ if (raw_length != SESSION_EXPORT_RAW_LENGTH) {
+ session->last_error = OLM_BAD_SESSION_KEY;
+ return (size_t)-1;
+ }
+
+ _olm_decode_base64(session_key, session_key_length, key_buf);
+ result = _init_group_session_keys(session, key_buf, 1);
+ _olm_unset(key_buf, SESSION_EXPORT_RAW_LENGTH);
+ return result;
+}
+
+static size_t raw_pickle_length(
+ const OlmInboundGroupSession *session
+) {
+ size_t length = 0;
+ length += _olm_pickle_uint32_length(PICKLE_VERSION);
+ length += megolm_pickle_length(&session->initial_ratchet);
+ length += megolm_pickle_length(&session->latest_ratchet);
+ length += _olm_pickle_ed25519_public_key_length(&session->signing_key);
+ length += _olm_pickle_bool_length(session->signing_key_verified);
+ return length;
+}
+
+size_t olm_pickle_inbound_group_session_length(
+ const OlmInboundGroupSession *session
+) {
+ return _olm_enc_output_length(raw_pickle_length(session));
+}
+
+size_t olm_pickle_inbound_group_session(
+ OlmInboundGroupSession *session,
+ void const * key, size_t key_length,
+ void * pickled, size_t pickled_length
+) {
+ size_t raw_length = raw_pickle_length(session);
+ uint8_t *pos;
+
+ if (pickled_length < _olm_enc_output_length(raw_length)) {
+ session->last_error = OLM_OUTPUT_BUFFER_TOO_SMALL;
+ return (size_t)-1;
+ }
+
+ pos = _olm_enc_output_pos(pickled, raw_length);
+ pos = _olm_pickle_uint32(pos, PICKLE_VERSION);
+ pos = megolm_pickle(&session->initial_ratchet, pos);
+ pos = megolm_pickle(&session->latest_ratchet, pos);
+ pos = _olm_pickle_ed25519_public_key(pos, &session->signing_key);
+ pos = _olm_pickle_bool(pos, session->signing_key_verified);
+
+ return _olm_enc_output(key, key_length, pickled, raw_length);
+}
+
+size_t olm_unpickle_inbound_group_session(
+ OlmInboundGroupSession *session,
+ void const * key, size_t key_length,
+ void * pickled, size_t pickled_length
+) {
+ const uint8_t *pos;
+ const uint8_t *end;
+ uint32_t pickle_version;
+
+ size_t raw_length = _olm_enc_input(
+ key, key_length, pickled, pickled_length, &(session->last_error)
+ );
+ if (raw_length == (size_t)-1) {
+ return raw_length;
+ }
+
+ pos = pickled;
+ end = pos + raw_length;
+
+ pos = _olm_unpickle_uint32(pos, end, &pickle_version);
+ FAIL_ON_CORRUPTED_PICKLE(pos, session);
+
+ if (pickle_version < 1 || pickle_version > PICKLE_VERSION) {
+ session->last_error = OLM_UNKNOWN_PICKLE_VERSION;
+ return (size_t)-1;
+ }
+
+ pos = megolm_unpickle(&session->initial_ratchet, pos, end);
+ FAIL_ON_CORRUPTED_PICKLE(pos, session);
+
+ pos = megolm_unpickle(&session->latest_ratchet, pos, end);
+ FAIL_ON_CORRUPTED_PICKLE(pos, session);
+
+ pos = _olm_unpickle_ed25519_public_key(pos, end, &session->signing_key);
+ FAIL_ON_CORRUPTED_PICKLE(pos, session);
+
+ if (pickle_version == 1) {
+ /* pickle v1 had no signing_key_verified field (all keyshares were
+ * verified at import time) */
+ session->signing_key_verified = 1;
+ } else {
+ pos = _olm_unpickle_bool(pos, end, &(session->signing_key_verified));
+ }
+ FAIL_ON_CORRUPTED_PICKLE(pos, session);
+
+ if (pos != end) {
+ /* Input was longer than expected. */
+ session->last_error = OLM_PICKLE_EXTRA_DATA;
+ return (size_t)-1;
+ }
+
+ return pickled_length;
+}
+
+/**
+ * get the max plaintext length in an un-base64-ed message
+ */
+static size_t _decrypt_max_plaintext_length(
+ OlmInboundGroupSession *session,
+ uint8_t * message, size_t message_length
+) {
+ struct _OlmDecodeGroupMessageResults decoded_results;
+
+ _olm_decode_group_message(
+ message, message_length,
+ megolm_cipher->ops->mac_length(megolm_cipher),
+ ED25519_SIGNATURE_LENGTH,
+ &decoded_results);
+
+ if (decoded_results.version != OLM_PROTOCOL_VERSION) {
+ session->last_error = OLM_BAD_MESSAGE_VERSION;
+ return (size_t)-1;
+ }
+
+ if (!decoded_results.ciphertext) {
+ session->last_error = OLM_BAD_MESSAGE_FORMAT;
+ return (size_t)-1;
+ }
+
+ return megolm_cipher->ops->decrypt_max_plaintext_length(
+ megolm_cipher, decoded_results.ciphertext_length);
+}
+
+size_t olm_group_decrypt_max_plaintext_length(
+ OlmInboundGroupSession *session,
+ uint8_t * message, size_t message_length
+) {
+ size_t raw_length;
+
+ raw_length = _olm_decode_base64(message, message_length, message);
+ if (raw_length == (size_t)-1) {
+ session->last_error = OLM_INVALID_BASE64;
+ return (size_t)-1;
+ }
+
+ return _decrypt_max_plaintext_length(
+ session, message, raw_length
+ );
+}
+
+/**
+ * get a copy of the megolm ratchet, advanced
+ * to the relevant index. Returns 0 on success, -1 on error
+ */
+static size_t _get_megolm(
+ OlmInboundGroupSession *session, uint32_t message_index, Megolm *result
+) {
+ /* pick a megolm instance to use. If we're at or beyond the latest ratchet
+ * value, use that */
+ if ((message_index - session->latest_ratchet.counter) < (1U << 31)) {
+ megolm_advance_to(&session->latest_ratchet, message_index);
+ *result = session->latest_ratchet;
+ return 0;
+ } else if ((message_index - session->initial_ratchet.counter) >= (1U << 31)) {
+ /* the counter is before our intial ratchet - we can't decode this. */
+ session->last_error = OLM_UNKNOWN_MESSAGE_INDEX;
+ return (size_t)-1;
+ } else {
+ /* otherwise, start from the initial megolm. Take a copy so that we
+ * don't overwrite the initial megolm */
+ *result = session->initial_ratchet;
+ megolm_advance_to(result, message_index);
+ return 0;
+ }
+}
+
+/**
+ * decrypt an un-base64-ed message
+ */
+static size_t _decrypt(
+ OlmInboundGroupSession *session,
+ uint8_t * message, size_t message_length,
+ uint8_t * plaintext, size_t max_plaintext_length,
+ uint32_t * message_index
+) {
+ struct _OlmDecodeGroupMessageResults decoded_results;
+ size_t max_length, r;
+ Megolm megolm;
+
+ _olm_decode_group_message(
+ message, message_length,
+ megolm_cipher->ops->mac_length(megolm_cipher),
+ ED25519_SIGNATURE_LENGTH,
+ &decoded_results);
+
+ if (decoded_results.version != OLM_PROTOCOL_VERSION) {
+ session->last_error = OLM_BAD_MESSAGE_VERSION;
+ return (size_t)-1;
+ }
+
+ if (!decoded_results.has_message_index || !decoded_results.ciphertext) {
+ session->last_error = OLM_BAD_MESSAGE_FORMAT;
+ return (size_t)-1;
+ }
+
+ if (message_index != NULL) {
+ *message_index = decoded_results.message_index;
+ }
+
+ /* verify the signature. We could do this before decoding the message, but
+ * we allow for the possibility of future protocol versions which use a
+ * different signing mechanism; we would rather throw "BAD_MESSAGE_VERSION"
+ * than "BAD_SIGNATURE" in this case.
+ */
+ message_length -= ED25519_SIGNATURE_LENGTH;
+ r = _olm_crypto_ed25519_verify(
+ &session->signing_key,
+ message, message_length,
+ message + message_length
+ );
+ if (!r) {
+ session->last_error = OLM_BAD_SIGNATURE;
+ return (size_t)-1;
+ }
+
+ max_length = megolm_cipher->ops->decrypt_max_plaintext_length(
+ megolm_cipher,
+ decoded_results.ciphertext_length
+ );
+ if (max_plaintext_length < max_length) {
+ session->last_error = OLM_OUTPUT_BUFFER_TOO_SMALL;
+ return (size_t)-1;
+ }
+
+ r = _get_megolm(session, decoded_results.message_index, &megolm);
+ if (r == (size_t)-1) {
+ return r;
+ }
+
+ /* now try checking the mac, and decrypting */
+ r = megolm_cipher->ops->decrypt(
+ megolm_cipher,
+ megolm_get_data(&megolm), MEGOLM_RATCHET_LENGTH,
+ message, message_length,
+ decoded_results.ciphertext, decoded_results.ciphertext_length,
+ plaintext, max_plaintext_length
+ );
+
+ _olm_unset(&megolm, sizeof(megolm));
+ if (r == (size_t)-1) {
+ session->last_error = OLM_BAD_MESSAGE_MAC;
+ return r;
+ }
+
+ /* once we have successfully decrypted a message, set a flag to say the
+ * session appears valid. */
+ session->signing_key_verified = 1;
+
+ return r;
+}
+
+size_t olm_group_decrypt(
+ OlmInboundGroupSession *session,
+ uint8_t * message, size_t message_length,
+ uint8_t * plaintext, size_t max_plaintext_length,
+ uint32_t * message_index
+) {
+ size_t raw_message_length;
+
+ raw_message_length = _olm_decode_base64(message, message_length, message);
+ if (raw_message_length == (size_t)-1) {
+ session->last_error = OLM_INVALID_BASE64;
+ return (size_t)-1;
+ }
+
+ return _decrypt(
+ session, message, raw_message_length,
+ plaintext, max_plaintext_length,
+ message_index
+ );
+}
+
+size_t olm_inbound_group_session_id_length(
+ const OlmInboundGroupSession *session
+) {
+ return _olm_encode_base64_length(GROUP_SESSION_ID_LENGTH);
+}
+
+size_t olm_inbound_group_session_id(
+ OlmInboundGroupSession *session,
+ uint8_t * id, size_t id_length
+) {
+ if (id_length < olm_inbound_group_session_id_length(session)) {
+ session->last_error = OLM_OUTPUT_BUFFER_TOO_SMALL;
+ return (size_t)-1;
+ }
+
+ return _olm_encode_base64(
+ session->signing_key.public_key, GROUP_SESSION_ID_LENGTH, id
+ );
+}
+
+uint32_t olm_inbound_group_session_first_known_index(
+ const OlmInboundGroupSession *session
+) {
+ return session->initial_ratchet.counter;
+}
+
+int olm_inbound_group_session_is_verified(
+ const OlmInboundGroupSession *session
+) {
+ return session->signing_key_verified;
+}
+
+size_t olm_export_inbound_group_session_length(
+ const OlmInboundGroupSession *session
+) {
+ return _olm_encode_base64_length(SESSION_EXPORT_RAW_LENGTH);
+}
+
+size_t olm_export_inbound_group_session(
+ OlmInboundGroupSession *session,
+ uint8_t * key, size_t key_length, uint32_t message_index
+) {
+ uint8_t *raw;
+ uint8_t *ptr;
+ Megolm megolm;
+ size_t r;
+ size_t encoded_length = olm_export_inbound_group_session_length(session);
+
+ if (key_length < encoded_length) {
+ session->last_error = OLM_OUTPUT_BUFFER_TOO_SMALL;
+ return (size_t)-1;
+ }
+
+ r = _get_megolm(session, message_index, &megolm);
+ if (r == (size_t)-1) {
+ return r;
+ }
+
+ /* put the raw data at the end of the output buffer. */
+ raw = ptr = key + encoded_length - SESSION_EXPORT_RAW_LENGTH;
+ *ptr++ = SESSION_EXPORT_VERSION;
+
+ // Encode message index as a big endian 32-bit number.
+ for (unsigned i = 0; i < 4; i++) {
+ *ptr++ = 0xFF & (message_index >> 24); message_index <<= 8;
+ }
+
+ memcpy(ptr, megolm_get_data(&megolm), MEGOLM_RATCHET_LENGTH);
+ ptr += MEGOLM_RATCHET_LENGTH;
+
+ memcpy(
+ ptr, session->signing_key.public_key,
+ ED25519_PUBLIC_KEY_LENGTH
+ );
+ ptr += ED25519_PUBLIC_KEY_LENGTH;
+
+ return _olm_encode_base64(raw, SESSION_EXPORT_RAW_LENGTH, key);
+}
--- /dev/null
+/* Copyright 2016 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include "olm/megolm.h"
+
+#include <string.h>
+
+#include "olm/cipher.h"
+#include "olm/crypto.h"
+#include "olm/pickle.h"
+
+static const struct _olm_cipher_aes_sha_256 MEGOLM_CIPHER =
+ OLM_CIPHER_INIT_AES_SHA_256("MEGOLM_KEYS");
+const struct _olm_cipher *megolm_cipher = OLM_CIPHER_BASE(&MEGOLM_CIPHER);
+
+/* the seeds used in the HMAC-SHA-256 functions for each part of the ratchet.
+ */
+#define HASH_KEY_SEED_LENGTH 1
+static uint8_t HASH_KEY_SEEDS[MEGOLM_RATCHET_PARTS][HASH_KEY_SEED_LENGTH] = {
+ {0x00},
+ {0x01},
+ {0x02},
+ {0x03}
+};
+
+static void rehash_part(
+ uint8_t data[MEGOLM_RATCHET_PARTS][MEGOLM_RATCHET_PART_LENGTH],
+ int rehash_from_part, int rehash_to_part
+) {
+ _olm_crypto_hmac_sha256(
+ data[rehash_from_part],
+ MEGOLM_RATCHET_PART_LENGTH,
+ HASH_KEY_SEEDS[rehash_to_part], HASH_KEY_SEED_LENGTH,
+ data[rehash_to_part]
+ );
+}
+
+
+
+void megolm_init(Megolm *megolm, uint8_t const *random_data, uint32_t counter) {
+ megolm->counter = counter;
+ memcpy(megolm->data, random_data, MEGOLM_RATCHET_LENGTH);
+}
+
+size_t megolm_pickle_length(const Megolm *megolm) {
+ size_t length = 0;
+ length += _olm_pickle_bytes_length(megolm_get_data(megolm), MEGOLM_RATCHET_LENGTH);
+ length += _olm_pickle_uint32_length(megolm->counter);
+ return length;
+
+}
+
+uint8_t * megolm_pickle(const Megolm *megolm, uint8_t *pos) {
+ pos = _olm_pickle_bytes(pos, megolm_get_data(megolm), MEGOLM_RATCHET_LENGTH);
+ pos = _olm_pickle_uint32(pos, megolm->counter);
+ return pos;
+}
+
+const uint8_t * megolm_unpickle(Megolm *megolm, const uint8_t *pos,
+ const uint8_t *end) {
+ pos = _olm_unpickle_bytes(pos, end, (uint8_t *)(megolm->data),
+ MEGOLM_RATCHET_LENGTH);
+ UNPICKLE_OK(pos);
+
+ pos = _olm_unpickle_uint32(pos, end, &megolm->counter);
+ UNPICKLE_OK(pos);
+
+ return pos;
+}
+
+/* simplistic implementation for a single step */
+void megolm_advance(Megolm *megolm) {
+ uint32_t mask = 0x00FFFFFF;
+ int h = 0;
+ int i;
+
+ megolm->counter++;
+
+ /* figure out how much we need to rekey */
+ while (h < (int)MEGOLM_RATCHET_PARTS) {
+ if (!(megolm->counter & mask))
+ break;
+ h++;
+ mask >>= 8;
+ }
+
+ /* now update R(h)...R(3) based on R(h) */
+ for (i = MEGOLM_RATCHET_PARTS-1; i >= h; i--) {
+ rehash_part(megolm->data, h, i);
+ }
+}
+
+void megolm_advance_to(Megolm *megolm, uint32_t advance_to) {
+ int j;
+
+ /* starting with R0, see if we need to update each part of the hash */
+ for (j = 0; j < (int)MEGOLM_RATCHET_PARTS; j++) {
+ int shift = (MEGOLM_RATCHET_PARTS-j-1) * 8;
+ uint32_t mask = (~(uint32_t)0) << shift;
+ int k;
+
+ /* how many times do we need to rehash this part?
+ *
+ * '& 0xff' ensures we handle integer wraparound correctly
+ */
+ unsigned int steps =
+ ((advance_to >> shift) - (megolm->counter >> shift)) & 0xff;
+
+ if (steps == 0) {
+ /* deal with the edge case where megolm->counter is slightly larger
+ * than advance_to. This should only happen for R(0), and implies
+ * that advance_to has wrapped around and we need to advance R(0)
+ * 256 times.
+ */
+ if (advance_to < megolm->counter) {
+ steps = 0x100;
+ } else {
+ continue;
+ }
+ }
+
+ /* for all but the last step, we can just bump R(j) without regard
+ * to R(j+1)...R(3).
+ */
+ while (steps > 1) {
+ rehash_part(megolm->data, j, j);
+ steps --;
+ }
+
+ /* on the last step we also need to bump R(j+1)...R(3).
+ *
+ * (Theoretically, we could skip bumping R(j+2) if we're going to bump
+ * R(j+1) again, but the code to figure that out is a bit baroque and
+ * doesn't save us much).
+ */
+ for (k = 3; k >= j; k--) {
+ rehash_part(megolm->data, j, k);
+ }
+ megolm->counter = advance_to & mask;
+ }
+}
--- /dev/null
+/* Copyright 2015 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "olm/memory.hh"
+#include "olm/memory.h"
+
+void _olm_unset(
+ void volatile * buffer, size_t buffer_length
+) {
+ olm::unset(buffer, buffer_length);
+}
+
+void olm::unset(
+ void volatile * buffer, std::size_t buffer_length
+) {
+ char volatile * pos = reinterpret_cast<char volatile *>(buffer);
+ char volatile * end = pos + buffer_length;
+ while (pos != end) {
+ *(pos++) = 0;
+ }
+}
+
+
+bool olm::is_equal(
+ std::uint8_t const * buffer_a,
+ std::uint8_t const * buffer_b,
+ std::size_t length
+) {
+ std::uint8_t volatile result = 0;
+ while (length--) {
+ result |= (*(buffer_a++)) ^ (*(buffer_b++));
+ }
+ return result == 0;
+}
--- /dev/null
+/* Copyright 2015-2016 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "olm/message.hh"
+
+#include "olm/memory.hh"
+
+namespace {
+
+template<typename T>
+static std::size_t varint_length(
+ T value
+) {
+ std::size_t result = 1;
+ while (value >= 128U) {
+ ++result;
+ value >>= 7;
+ }
+ return result;
+}
+
+
+template<typename T>
+static std::uint8_t * varint_encode(
+ std::uint8_t * output,
+ T value
+) {
+ while (value >= 128U) {
+ *(output++) = (0x7F & value) | 0x80;
+ value >>= 7;
+ }
+ (*output++) = value;
+ return output;
+}
+
+
+template<typename T>
+static T varint_decode(
+ std::uint8_t const * varint_start,
+ std::uint8_t const * varint_end
+) {
+ T value = 0;
+ if (varint_end == varint_start) {
+ return 0;
+ }
+ do {
+ value <<= 7;
+ value |= 0x7F & *(--varint_end);
+ } while (varint_end != varint_start);
+ return value;
+}
+
+
+static std::uint8_t const * varint_skip(
+ std::uint8_t const * input,
+ std::uint8_t const * input_end
+) {
+ while (input != input_end) {
+ std::uint8_t tmp = *(input++);
+ if ((tmp & 0x80) == 0) {
+ return input;
+ }
+ }
+ return input;
+}
+
+
+static std::size_t varstring_length(
+ std::size_t string_length
+) {
+ return varint_length(string_length) + string_length;
+}
+
+static std::size_t const VERSION_LENGTH = 1;
+static std::uint8_t const RATCHET_KEY_TAG = 012;
+static std::uint8_t const COUNTER_TAG = 020;
+static std::uint8_t const CIPHERTEXT_TAG = 042;
+
+static std::uint8_t * encode(
+ std::uint8_t * pos,
+ std::uint8_t tag,
+ std::uint32_t value
+) {
+ *(pos++) = tag;
+ return varint_encode(pos, value);
+}
+
+static std::uint8_t * encode(
+ std::uint8_t * pos,
+ std::uint8_t tag,
+ std::uint8_t * & value, std::size_t value_length
+) {
+ *(pos++) = tag;
+ pos = varint_encode(pos, value_length);
+ value = pos;
+ return pos + value_length;
+}
+
+static std::uint8_t const * decode(
+ std::uint8_t const * pos, std::uint8_t const * end,
+ std::uint8_t tag,
+ std::uint32_t & value, bool & has_value
+) {
+ if (pos != end && *pos == tag) {
+ ++pos;
+ std::uint8_t const * value_start = pos;
+ pos = varint_skip(pos, end);
+ value = varint_decode<std::uint32_t>(value_start, pos);
+ has_value = true;
+ }
+ return pos;
+}
+
+
+static std::uint8_t const * decode(
+ std::uint8_t const * pos, std::uint8_t const * end,
+ std::uint8_t tag,
+ std::uint8_t const * & value, std::size_t & value_length
+) {
+ if (pos != end && *pos == tag) {
+ ++pos;
+ std::uint8_t const * len_start = pos;
+ pos = varint_skip(pos, end);
+ std::size_t len = varint_decode<std::size_t>(len_start, pos);
+ if (len > std::size_t(end - pos)) return end;
+ value = pos;
+ value_length = len;
+ pos += len;
+ }
+ return pos;
+}
+
+static std::uint8_t const * skip_unknown(
+ std::uint8_t const * pos, std::uint8_t const * end
+) {
+ if (pos != end) {
+ uint8_t tag = *pos;
+ if ((tag & 0x7) == 0) {
+ pos = varint_skip(pos, end);
+ pos = varint_skip(pos, end);
+ } else if ((tag & 0x7) == 2) {
+ pos = varint_skip(pos, end);
+ std::uint8_t const * len_start = pos;
+ pos = varint_skip(pos, end);
+ std::size_t len = varint_decode<std::size_t>(len_start, pos);
+ if (len > std::size_t(end - pos)) return end;
+ pos += len;
+ } else {
+ return end;
+ }
+ }
+ return pos;
+}
+
+} // namespace
+
+
+std::size_t olm::encode_message_length(
+ std::uint32_t counter,
+ std::size_t ratchet_key_length,
+ std::size_t ciphertext_length,
+ std::size_t mac_length
+) {
+ std::size_t length = VERSION_LENGTH;
+ length += 1 + varstring_length(ratchet_key_length);
+ length += 1 + varint_length(counter);
+ length += 1 + varstring_length(ciphertext_length);
+ length += mac_length;
+ return length;
+}
+
+
+void olm::encode_message(
+ olm::MessageWriter & writer,
+ std::uint8_t version,
+ std::uint32_t counter,
+ std::size_t ratchet_key_length,
+ std::size_t ciphertext_length,
+ std::uint8_t * output
+) {
+ std::uint8_t * pos = output;
+ *(pos++) = version;
+ pos = encode(pos, RATCHET_KEY_TAG, writer.ratchet_key, ratchet_key_length);
+ pos = encode(pos, COUNTER_TAG, counter);
+ pos = encode(pos, CIPHERTEXT_TAG, writer.ciphertext, ciphertext_length);
+}
+
+
+void olm::decode_message(
+ olm::MessageReader & reader,
+ std::uint8_t const * input, std::size_t input_length,
+ std::size_t mac_length
+) {
+ std::uint8_t const * pos = input;
+ std::uint8_t const * end = input + input_length - mac_length;
+ std::uint8_t const * unknown = nullptr;
+
+ reader.version = 0;
+ reader.has_counter = false;
+ reader.counter = 0;
+ reader.input = input;
+ reader.input_length = input_length;
+ reader.ratchet_key = nullptr;
+ reader.ratchet_key_length = 0;
+ reader.ciphertext = nullptr;
+ reader.ciphertext_length = 0;
+
+ if (input_length < mac_length) return;
+
+ if (pos == end) return;
+ reader.version = *(pos++);
+
+ while (pos != end) {
+ unknown = pos;
+ pos = decode(
+ pos, end, RATCHET_KEY_TAG,
+ reader.ratchet_key, reader.ratchet_key_length
+ );
+ pos = decode(
+ pos, end, COUNTER_TAG,
+ reader.counter, reader.has_counter
+ );
+ pos = decode(
+ pos, end, CIPHERTEXT_TAG,
+ reader.ciphertext, reader.ciphertext_length
+ );
+ if (unknown == pos) {
+ pos = skip_unknown(pos, end);
+ }
+ }
+}
+
+
+namespace {
+
+static std::uint8_t const ONE_TIME_KEY_ID_TAG = 012;
+static std::uint8_t const BASE_KEY_TAG = 022;
+static std::uint8_t const IDENTITY_KEY_TAG = 032;
+static std::uint8_t const MESSAGE_TAG = 042;
+
+} // namespace
+
+
+std::size_t olm::encode_one_time_key_message_length(
+ std::size_t one_time_key_length,
+ std::size_t identity_key_length,
+ std::size_t base_key_length,
+ std::size_t message_length
+) {
+ std::size_t length = VERSION_LENGTH;
+ length += 1 + varstring_length(one_time_key_length);
+ length += 1 + varstring_length(identity_key_length);
+ length += 1 + varstring_length(base_key_length);
+ length += 1 + varstring_length(message_length);
+ return length;
+}
+
+
+void olm::encode_one_time_key_message(
+ olm::PreKeyMessageWriter & writer,
+ std::uint8_t version,
+ std::size_t identity_key_length,
+ std::size_t base_key_length,
+ std::size_t one_time_key_length,
+ std::size_t message_length,
+ std::uint8_t * output
+) {
+ std::uint8_t * pos = output;
+ *(pos++) = version;
+ pos = encode(pos, ONE_TIME_KEY_ID_TAG, writer.one_time_key, one_time_key_length);
+ pos = encode(pos, BASE_KEY_TAG, writer.base_key, base_key_length);
+ pos = encode(pos, IDENTITY_KEY_TAG, writer.identity_key, identity_key_length);
+ pos = encode(pos, MESSAGE_TAG, writer.message, message_length);
+}
+
+
+void olm::decode_one_time_key_message(
+ PreKeyMessageReader & reader,
+ std::uint8_t const * input, std::size_t input_length
+) {
+ std::uint8_t const * pos = input;
+ std::uint8_t const * end = input + input_length;
+ std::uint8_t const * unknown = nullptr;
+
+ reader.version = 0;
+ reader.one_time_key = nullptr;
+ reader.one_time_key_length = 0;
+ reader.identity_key = nullptr;
+ reader.identity_key_length = 0;
+ reader.base_key = nullptr;
+ reader.base_key_length = 0;
+ reader.message = nullptr;
+ reader.message_length = 0;
+
+ if (pos == end) return;
+ reader.version = *(pos++);
+
+ while (pos != end) {
+ unknown = pos;
+ pos = decode(
+ pos, end, ONE_TIME_KEY_ID_TAG,
+ reader.one_time_key, reader.one_time_key_length
+ );
+ pos = decode(
+ pos, end, BASE_KEY_TAG,
+ reader.base_key, reader.base_key_length
+ );
+ pos = decode(
+ pos, end, IDENTITY_KEY_TAG,
+ reader.identity_key, reader.identity_key_length
+ );
+ pos = decode(
+ pos, end, MESSAGE_TAG,
+ reader.message, reader.message_length
+ );
+ if (unknown == pos) {
+ pos = skip_unknown(pos, end);
+ }
+ }
+}
+
+
+
+static const std::uint8_t GROUP_MESSAGE_INDEX_TAG = 010;
+static const std::uint8_t GROUP_CIPHERTEXT_TAG = 022;
+
+size_t _olm_encode_group_message_length(
+ uint32_t message_index,
+ size_t ciphertext_length,
+ size_t mac_length,
+ size_t signature_length
+) {
+ size_t length = VERSION_LENGTH;
+ length += 1 + varint_length(message_index);
+ length += 1 + varstring_length(ciphertext_length);
+ length += mac_length;
+ length += signature_length;
+ return length;
+}
+
+
+size_t _olm_encode_group_message(
+ uint8_t version,
+ uint32_t message_index,
+ size_t ciphertext_length,
+ uint8_t *output,
+ uint8_t **ciphertext_ptr
+) {
+ std::uint8_t * pos = output;
+
+ *(pos++) = version;
+ pos = encode(pos, GROUP_MESSAGE_INDEX_TAG, message_index);
+ pos = encode(pos, GROUP_CIPHERTEXT_TAG, *ciphertext_ptr, ciphertext_length);
+ return pos-output;
+}
+
+void _olm_decode_group_message(
+ const uint8_t *input, size_t input_length,
+ size_t mac_length, size_t signature_length,
+ struct _OlmDecodeGroupMessageResults *results
+) {
+ std::uint8_t const * pos = input;
+ std::size_t trailer_length = mac_length + signature_length;
+ std::uint8_t const * end = input + input_length - trailer_length;
+ std::uint8_t const * unknown = nullptr;
+
+ bool has_message_index = false;
+ results->version = 0;
+ results->message_index = 0;
+ results->has_message_index = (int)has_message_index;
+ results->ciphertext = nullptr;
+ results->ciphertext_length = 0;
+
+ if (input_length < trailer_length) return;
+
+ if (pos == end) return;
+ results->version = *(pos++);
+
+ while (pos != end) {
+ unknown = pos;
+ pos = decode(
+ pos, end, GROUP_MESSAGE_INDEX_TAG,
+ results->message_index, has_message_index
+ );
+ pos = decode(
+ pos, end, GROUP_CIPHERTEXT_TAG,
+ results->ciphertext, results->ciphertext_length
+ );
+ if (unknown == pos) {
+ pos = skip_unknown(pos, end);
+ }
+ }
+
+ results->has_message_index = (int)has_message_index;
+}
--- /dev/null
+/* Copyright 2015 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "olm/olm.h"
+#include "olm/session.hh"
+#include "olm/account.hh"
+#include "olm/cipher.h"
+#include "olm/pickle_encoding.h"
+#include "olm/utility.hh"
+#include "olm/base64.hh"
+#include "olm/memory.hh"
+
+#include <new>
+#include <cstring>
+
+namespace {
+
+static OlmAccount * to_c(olm::Account * account) {
+ return reinterpret_cast<OlmAccount *>(account);
+}
+
+static OlmSession * to_c(olm::Session * session) {
+ return reinterpret_cast<OlmSession *>(session);
+}
+
+static OlmUtility * to_c(olm::Utility * utility) {
+ return reinterpret_cast<OlmUtility *>(utility);
+}
+
+static olm::Account * from_c(OlmAccount * account) {
+ return reinterpret_cast<olm::Account *>(account);
+}
+
+static const olm::Account * from_c(OlmAccount const * account) {
+ return reinterpret_cast<olm::Account const *>(account);
+}
+
+static olm::Session * from_c(OlmSession * session) {
+ return reinterpret_cast<olm::Session *>(session);
+}
+
+static const olm::Session * from_c(OlmSession const * session) {
+ return reinterpret_cast<const olm::Session *>(session);
+}
+
+static olm::Utility * from_c(OlmUtility * utility) {
+ return reinterpret_cast<olm::Utility *>(utility);
+}
+
+static const olm::Utility * from_c(OlmUtility const * utility) {
+ return reinterpret_cast<const olm::Utility *>(utility);
+}
+
+static std::uint8_t * from_c(void * bytes) {
+ return reinterpret_cast<std::uint8_t *>(bytes);
+}
+
+static std::uint8_t const * from_c(void const * bytes) {
+ return reinterpret_cast<std::uint8_t const *>(bytes);
+}
+
+std::size_t b64_output_length(
+ size_t raw_length
+) {
+ return olm::encode_base64_length(raw_length);
+}
+
+std::uint8_t * b64_output_pos(
+ std::uint8_t * output,
+ size_t raw_length
+) {
+ return output + olm::encode_base64_length(raw_length) - raw_length;
+}
+
+std::size_t b64_output(
+ std::uint8_t * output, size_t raw_length
+) {
+ std::size_t base64_length = olm::encode_base64_length(raw_length);
+ std::uint8_t * raw_output = output + base64_length - raw_length;
+ olm::encode_base64(raw_output, raw_length, output);
+ return base64_length;
+}
+
+std::size_t b64_input(
+ std::uint8_t * input, size_t b64_length,
+ OlmErrorCode & last_error
+) {
+ std::size_t raw_length = olm::decode_base64_length(b64_length);
+ if (raw_length == std::size_t(-1)) {
+ last_error = OlmErrorCode::OLM_INVALID_BASE64;
+ return std::size_t(-1);
+ }
+ olm::decode_base64(input, b64_length, input);
+ return raw_length;
+}
+
+} // namespace
+
+
+extern "C" {
+
+void olm_get_library_version(uint8_t *major, uint8_t *minor, uint8_t *patch) {
+ if (major != NULL) *major = OLMLIB_VERSION_MAJOR;
+ if (minor != NULL) *minor = OLMLIB_VERSION_MINOR;
+ if (patch != NULL) *patch = OLMLIB_VERSION_PATCH;
+}
+
+size_t olm_error(void) {
+ return std::size_t(-1);
+}
+
+
+const char * olm_account_last_error(
+ const OlmAccount * account
+) {
+ auto error = from_c(account)->last_error;
+ return _olm_error_to_string(error);
+}
+
+enum OlmErrorCode olm_account_last_error_code(
+ const OlmAccount * account
+) {
+ return from_c(account)->last_error;
+}
+
+const char * olm_session_last_error(
+ const OlmSession * session
+) {
+ auto error = from_c(session)->last_error;
+ return _olm_error_to_string(error);
+}
+
+enum OlmErrorCode olm_session_last_error_code(
+ OlmSession const * session
+) {
+ return from_c(session)->last_error;
+}
+
+const char * olm_utility_last_error(
+ OlmUtility const * utility
+) {
+ auto error = from_c(utility)->last_error;
+ return _olm_error_to_string(error);
+}
+
+enum OlmErrorCode olm_utility_last_error_code(
+ OlmUtility const * utility
+) {
+ return from_c(utility)->last_error;
+}
+
+size_t olm_account_size(void) {
+ return sizeof(olm::Account);
+}
+
+
+size_t olm_session_size(void) {
+ return sizeof(olm::Session);
+}
+
+size_t olm_utility_size(void) {
+ return sizeof(olm::Utility);
+}
+
+OlmAccount * olm_account(
+ void * memory
+) {
+ olm::unset(memory, sizeof(olm::Account));
+ return to_c(new(memory) olm::Account());
+}
+
+
+OlmSession * olm_session(
+ void * memory
+) {
+ olm::unset(memory, sizeof(olm::Session));
+ return to_c(new(memory) olm::Session());
+}
+
+
+OlmUtility * olm_utility(
+ void * memory
+) {
+ olm::unset(memory, sizeof(olm::Utility));
+ return to_c(new(memory) olm::Utility());
+}
+
+
+size_t olm_clear_account(
+ OlmAccount * account
+) {
+ /* Clear the memory backing the account */
+ olm::unset(account, sizeof(olm::Account));
+ /* Initialise a fresh account object in case someone tries to use it */
+ new(account) olm::Account();
+ return sizeof(olm::Account);
+}
+
+
+size_t olm_clear_session(
+ OlmSession * session
+) {
+ /* Clear the memory backing the session */
+ olm::unset(session, sizeof(olm::Session));
+ /* Initialise a fresh session object in case someone tries to use it */
+ new(session) olm::Session();
+ return sizeof(olm::Session);
+}
+
+
+size_t olm_clear_utility(
+ OlmUtility * utility
+) {
+ /* Clear the memory backing the session */
+ olm::unset(utility, sizeof(olm::Utility));
+ /* Initialise a fresh session object in case someone tries to use it */
+ new(utility) olm::Utility();
+ return sizeof(olm::Utility);
+}
+
+
+size_t olm_pickle_account_length(
+ OlmAccount const * account
+) {
+ return _olm_enc_output_length(pickle_length(*from_c(account)));
+}
+
+
+size_t olm_pickle_session_length(
+ OlmSession const * session
+) {
+ return _olm_enc_output_length(pickle_length(*from_c(session)));
+}
+
+
+size_t olm_pickle_account(
+ OlmAccount * account,
+ void const * key, size_t key_length,
+ void * pickled, size_t pickled_length
+) {
+ olm::Account & object = *from_c(account);
+ std::size_t raw_length = pickle_length(object);
+ if (pickled_length < _olm_enc_output_length(raw_length)) {
+ object.last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
+ return size_t(-1);
+ }
+ pickle(_olm_enc_output_pos(from_c(pickled), raw_length), object);
+ return _olm_enc_output(from_c(key), key_length, from_c(pickled), raw_length);
+}
+
+
+size_t olm_pickle_session(
+ OlmSession * session,
+ void const * key, size_t key_length,
+ void * pickled, size_t pickled_length
+) {
+ olm::Session & object = *from_c(session);
+ std::size_t raw_length = pickle_length(object);
+ if (pickled_length < _olm_enc_output_length(raw_length)) {
+ object.last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
+ return size_t(-1);
+ }
+ pickle(_olm_enc_output_pos(from_c(pickled), raw_length), object);
+ return _olm_enc_output(from_c(key), key_length, from_c(pickled), raw_length);
+}
+
+
+size_t olm_unpickle_account(
+ OlmAccount * account,
+ void const * key, size_t key_length,
+ void * pickled, size_t pickled_length
+) {
+ olm::Account & object = *from_c(account);
+ std::uint8_t * input = from_c(pickled);
+ std::size_t raw_length = _olm_enc_input(
+ from_c(key), key_length, input, pickled_length, &object.last_error
+ );
+ if (raw_length == std::size_t(-1)) {
+ return std::size_t(-1);
+ }
+
+ std::uint8_t const * pos = input;
+ std::uint8_t const * end = pos + raw_length;
+
+ pos = unpickle(pos, end, object);
+
+ if (!pos) {
+ /* Input was corrupted. */
+ if (object.last_error == OlmErrorCode::OLM_SUCCESS) {
+ object.last_error = OlmErrorCode::OLM_CORRUPTED_PICKLE;
+ }
+ return std::size_t(-1);
+ } else if (pos != end) {
+ /* Input was longer than expected. */
+ object.last_error = OlmErrorCode::OLM_PICKLE_EXTRA_DATA;
+ return std::size_t(-1);
+ }
+
+ return pickled_length;
+}
+
+
+size_t olm_unpickle_session(
+ OlmSession * session,
+ void const * key, size_t key_length,
+ void * pickled, size_t pickled_length
+) {
+ olm::Session & object = *from_c(session);
+ std::uint8_t * input = from_c(pickled);
+ std::size_t raw_length = _olm_enc_input(
+ from_c(key), key_length, input, pickled_length, &object.last_error
+ );
+ if (raw_length == std::size_t(-1)) {
+ return std::size_t(-1);
+ }
+
+ std::uint8_t const * pos = input;
+ std::uint8_t const * end = pos + raw_length;
+
+ pos = unpickle(pos, end, object);
+
+ if (!pos) {
+ /* Input was corrupted. */
+ if (object.last_error == OlmErrorCode::OLM_SUCCESS) {
+ object.last_error = OlmErrorCode::OLM_CORRUPTED_PICKLE;
+ }
+ return std::size_t(-1);
+ } else if (pos != end) {
+ /* Input was longer than expected. */
+ object.last_error = OlmErrorCode::OLM_PICKLE_EXTRA_DATA;
+ return std::size_t(-1);
+ }
+
+ return pickled_length;
+}
+
+
+size_t olm_create_account_random_length(
+ OlmAccount const * account
+) {
+ return from_c(account)->new_account_random_length();
+}
+
+
+size_t olm_create_account(
+ OlmAccount * account,
+ void * random, size_t random_length
+) {
+ size_t result = from_c(account)->new_account(from_c(random), random_length);
+ olm::unset(random, random_length);
+ return result;
+}
+
+
+size_t olm_account_identity_keys_length(
+ OlmAccount const * account
+) {
+ return from_c(account)->get_identity_json_length();
+}
+
+
+size_t olm_account_identity_keys(
+ OlmAccount * account,
+ void * identity_keys, size_t identity_key_length
+) {
+ return from_c(account)->get_identity_json(
+ from_c(identity_keys), identity_key_length
+ );
+}
+
+
+size_t olm_account_signature_length(
+ OlmAccount const * account
+) {
+ return b64_output_length(from_c(account)->signature_length());
+}
+
+
+size_t olm_account_sign(
+ OlmAccount * account,
+ void const * message, size_t message_length,
+ void * signature, size_t signature_length
+) {
+ std::size_t raw_length = from_c(account)->signature_length();
+ if (signature_length < b64_output_length(raw_length)) {
+ from_c(account)->last_error =
+ OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
+ return std::size_t(-1);
+ }
+ from_c(account)->sign(
+ from_c(message), message_length,
+ b64_output_pos(from_c(signature), raw_length), raw_length
+ );
+ return b64_output(from_c(signature), raw_length);
+}
+
+
+size_t olm_account_one_time_keys_length(
+ OlmAccount const * account
+) {
+ return from_c(account)->get_one_time_keys_json_length();
+}
+
+
+size_t olm_account_one_time_keys(
+ OlmAccount * account,
+ void * one_time_keys_json, size_t one_time_key_json_length
+) {
+ return from_c(account)->get_one_time_keys_json(
+ from_c(one_time_keys_json), one_time_key_json_length
+ );
+}
+
+
+size_t olm_account_mark_keys_as_published(
+ OlmAccount * account
+) {
+ return from_c(account)->mark_keys_as_published();
+}
+
+
+size_t olm_account_max_number_of_one_time_keys(
+ OlmAccount const * account
+) {
+ return from_c(account)->max_number_of_one_time_keys();
+}
+
+
+size_t olm_account_generate_one_time_keys_random_length(
+ OlmAccount const * account,
+ size_t number_of_keys
+) {
+ return from_c(account)->generate_one_time_keys_random_length(number_of_keys);
+}
+
+
+size_t olm_account_generate_one_time_keys(
+ OlmAccount * account,
+ size_t number_of_keys,
+ void * random, size_t random_length
+) {
+ size_t result = from_c(account)->generate_one_time_keys(
+ number_of_keys,
+ from_c(random), random_length
+ );
+ olm::unset(random, random_length);
+ return result;
+}
+
+
+size_t olm_account_generate_fallback_key_random_length(
+ OlmAccount const * account
+) {
+ return from_c(account)->generate_fallback_key_random_length();
+}
+
+
+size_t olm_account_generate_fallback_key(
+ OlmAccount * account,
+ void * random, size_t random_length
+) {
+ size_t result = from_c(account)->generate_fallback_key(
+ from_c(random), random_length
+ );
+ olm::unset(random, random_length);
+ return result;
+}
+
+
+size_t olm_account_fallback_key_length(
+ OlmAccount const * account
+) {
+ return from_c(account)->get_fallback_key_json_length();
+}
+
+
+size_t olm_account_fallback_key(
+ OlmAccount * account,
+ void * fallback_key_json, size_t fallback_key_json_length
+) {
+ return from_c(account)->get_fallback_key_json(
+ from_c(fallback_key_json), fallback_key_json_length
+ );
+}
+
+
+size_t olm_account_unpublished_fallback_key_length(
+ OlmAccount const * account
+) {
+ return from_c(account)->get_unpublished_fallback_key_json_length();
+}
+
+
+size_t olm_account_unpublished_fallback_key(
+ OlmAccount * account,
+ void * fallback_key_json, size_t fallback_key_json_length
+) {
+ return from_c(account)->get_unpublished_fallback_key_json(
+ from_c(fallback_key_json), fallback_key_json_length
+ );
+}
+
+
+void olm_account_forget_old_fallback_key(
+ OlmAccount * account
+) {
+ return from_c(account)->forget_old_fallback_key();
+}
+
+
+size_t olm_create_outbound_session_random_length(
+ OlmSession const * session
+) {
+ return from_c(session)->new_outbound_session_random_length();
+}
+
+
+size_t olm_create_outbound_session(
+ OlmSession * session,
+ OlmAccount const * account,
+ void const * their_identity_key, size_t their_identity_key_length,
+ void const * their_one_time_key, size_t their_one_time_key_length,
+ void * random, size_t random_length
+) {
+ std::uint8_t const * id_key = from_c(their_identity_key);
+ std::uint8_t const * ot_key = from_c(their_one_time_key);
+ std::size_t id_key_length = their_identity_key_length;
+ std::size_t ot_key_length = their_one_time_key_length;
+
+ if (olm::decode_base64_length(id_key_length) != CURVE25519_KEY_LENGTH
+ || olm::decode_base64_length(ot_key_length) != CURVE25519_KEY_LENGTH
+ ) {
+ from_c(session)->last_error = OlmErrorCode::OLM_INVALID_BASE64;
+ return std::size_t(-1);
+ }
+ _olm_curve25519_public_key identity_key;
+ _olm_curve25519_public_key one_time_key;
+
+ olm::decode_base64(id_key, id_key_length, identity_key.public_key);
+ olm::decode_base64(ot_key, ot_key_length, one_time_key.public_key);
+
+ size_t result = from_c(session)->new_outbound_session(
+ *from_c(account), identity_key, one_time_key,
+ from_c(random), random_length
+ );
+ olm::unset(random, random_length);
+ return result;
+}
+
+
+size_t olm_create_inbound_session(
+ OlmSession * session,
+ OlmAccount * account,
+ void * one_time_key_message, size_t message_length
+) {
+ std::size_t raw_length = b64_input(
+ from_c(one_time_key_message), message_length, from_c(session)->last_error
+ );
+ if (raw_length == std::size_t(-1)) {
+ return std::size_t(-1);
+ }
+ return from_c(session)->new_inbound_session(
+ *from_c(account), nullptr, from_c(one_time_key_message), raw_length
+ );
+}
+
+
+size_t olm_create_inbound_session_from(
+ OlmSession * session,
+ OlmAccount * account,
+ void const * their_identity_key, size_t their_identity_key_length,
+ void * one_time_key_message, size_t message_length
+) {
+ std::uint8_t const * id_key = from_c(their_identity_key);
+ std::size_t id_key_length = their_identity_key_length;
+
+ if (olm::decode_base64_length(id_key_length) != CURVE25519_KEY_LENGTH) {
+ from_c(session)->last_error = OlmErrorCode::OLM_INVALID_BASE64;
+ return std::size_t(-1);
+ }
+ _olm_curve25519_public_key identity_key;
+ olm::decode_base64(id_key, id_key_length, identity_key.public_key);
+
+ std::size_t raw_length = b64_input(
+ from_c(one_time_key_message), message_length, from_c(session)->last_error
+ );
+ if (raw_length == std::size_t(-1)) {
+ return std::size_t(-1);
+ }
+ return from_c(session)->new_inbound_session(
+ *from_c(account), &identity_key,
+ from_c(one_time_key_message), raw_length
+ );
+}
+
+
+size_t olm_session_id_length(
+ OlmSession const * session
+) {
+ return b64_output_length(from_c(session)->session_id_length());
+}
+
+size_t olm_session_id(
+ OlmSession * session,
+ void * id, size_t id_length
+) {
+ std::size_t raw_length = from_c(session)->session_id_length();
+ if (id_length < b64_output_length(raw_length)) {
+ from_c(session)->last_error =
+ OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
+ return std::size_t(-1);
+ }
+ std::size_t result = from_c(session)->session_id(
+ b64_output_pos(from_c(id), raw_length), raw_length
+ );
+ if (result == std::size_t(-1)) {
+ return result;
+ }
+ return b64_output(from_c(id), raw_length);
+}
+
+
+int olm_session_has_received_message(
+ OlmSession const * session
+) {
+ return from_c(session)->received_message;
+}
+
+void olm_session_describe(
+ OlmSession * session, char *buf, size_t buflen
+) {
+ from_c(session)->describe(buf, buflen);
+}
+
+size_t olm_matches_inbound_session(
+ OlmSession * session,
+ void * one_time_key_message, size_t message_length
+) {
+ std::size_t raw_length = b64_input(
+ from_c(one_time_key_message), message_length, from_c(session)->last_error
+ );
+ if (raw_length == std::size_t(-1)) {
+ return std::size_t(-1);
+ }
+ bool matches = from_c(session)->matches_inbound_session(
+ nullptr, from_c(one_time_key_message), raw_length
+ );
+ return matches ? 1 : 0;
+}
+
+
+size_t olm_matches_inbound_session_from(
+ OlmSession * session,
+ void const * their_identity_key, size_t their_identity_key_length,
+ void * one_time_key_message, size_t message_length
+) {
+ std::uint8_t const * id_key = from_c(their_identity_key);
+ std::size_t id_key_length = their_identity_key_length;
+
+ if (olm::decode_base64_length(id_key_length) != CURVE25519_KEY_LENGTH) {
+ from_c(session)->last_error = OlmErrorCode::OLM_INVALID_BASE64;
+ return std::size_t(-1);
+ }
+ _olm_curve25519_public_key identity_key;
+ olm::decode_base64(id_key, id_key_length, identity_key.public_key);
+
+ std::size_t raw_length = b64_input(
+ from_c(one_time_key_message), message_length, from_c(session)->last_error
+ );
+ if (raw_length == std::size_t(-1)) {
+ return std::size_t(-1);
+ }
+ bool matches = from_c(session)->matches_inbound_session(
+ &identity_key, from_c(one_time_key_message), raw_length
+ );
+ return matches ? 1 : 0;
+}
+
+
+size_t olm_remove_one_time_keys(
+ OlmAccount * account,
+ OlmSession * session
+) {
+ size_t result = from_c(account)->remove_key(
+ from_c(session)->bob_one_time_key
+ );
+ if (result == std::size_t(-1)) {
+ from_c(account)->last_error = OlmErrorCode::OLM_BAD_MESSAGE_KEY_ID;
+ }
+ return result;
+}
+
+
+size_t olm_encrypt_message_type(
+ OlmSession const * session
+) {
+ return size_t(from_c(session)->encrypt_message_type());
+}
+
+
+size_t olm_encrypt_random_length(
+ OlmSession const * session
+) {
+ return from_c(session)->encrypt_random_length();
+}
+
+
+size_t olm_encrypt_message_length(
+ OlmSession const * session,
+ size_t plaintext_length
+) {
+ return b64_output_length(
+ from_c(session)->encrypt_message_length(plaintext_length)
+ );
+}
+
+
+size_t olm_encrypt(
+ OlmSession * session,
+ void const * plaintext, size_t plaintext_length,
+ void * random, size_t random_length,
+ void * message, size_t message_length
+) {
+ std::size_t raw_length = from_c(session)->encrypt_message_length(
+ plaintext_length
+ );
+ if (message_length < b64_output_length(raw_length)) {
+ from_c(session)->last_error =
+ OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
+ return std::size_t(-1);
+ }
+ std::size_t result = from_c(session)->encrypt(
+ from_c(plaintext), plaintext_length,
+ from_c(random), random_length,
+ b64_output_pos(from_c(message), raw_length), raw_length
+ );
+ olm::unset(random, random_length);
+ if (result == std::size_t(-1)) {
+ return result;
+ }
+ return b64_output(from_c(message), raw_length);
+}
+
+
+size_t olm_decrypt_max_plaintext_length(
+ OlmSession * session,
+ size_t message_type,
+ void * message, size_t message_length
+) {
+ std::size_t raw_length = b64_input(
+ from_c(message), message_length, from_c(session)->last_error
+ );
+ if (raw_length == std::size_t(-1)) {
+ return std::size_t(-1);
+ }
+ return from_c(session)->decrypt_max_plaintext_length(
+ olm::MessageType(message_type), from_c(message), raw_length
+ );
+}
+
+
+size_t olm_decrypt(
+ OlmSession * session,
+ size_t message_type,
+ void * message, size_t message_length,
+ void * plaintext, size_t max_plaintext_length
+) {
+ std::size_t raw_length = b64_input(
+ from_c(message), message_length, from_c(session)->last_error
+ );
+ if (raw_length == std::size_t(-1)) {
+ return std::size_t(-1);
+ }
+ return from_c(session)->decrypt(
+ olm::MessageType(message_type), from_c(message), raw_length,
+ from_c(plaintext), max_plaintext_length
+ );
+}
+
+
+size_t olm_sha256_length(
+ OlmUtility const * utility
+) {
+ return b64_output_length(from_c(utility)->sha256_length());
+}
+
+
+size_t olm_sha256(
+ OlmUtility * utility,
+ void const * input, size_t input_length,
+ void * output, size_t output_length
+) {
+ std::size_t raw_length = from_c(utility)->sha256_length();
+ if (output_length < b64_output_length(raw_length)) {
+ from_c(utility)->last_error =
+ OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
+ return std::size_t(-1);
+ }
+ std::size_t result = from_c(utility)->sha256(
+ from_c(input), input_length,
+ b64_output_pos(from_c(output), raw_length), raw_length
+ );
+ if (result == std::size_t(-1)) {
+ return result;
+ }
+ return b64_output(from_c(output), raw_length);
+}
+
+
+size_t olm_ed25519_verify(
+ OlmUtility * utility,
+ void const * key, size_t key_length,
+ void const * message, size_t message_length,
+ void * signature, size_t signature_length
+) {
+ if (olm::decode_base64_length(key_length) != CURVE25519_KEY_LENGTH) {
+ from_c(utility)->last_error = OlmErrorCode::OLM_INVALID_BASE64;
+ return std::size_t(-1);
+ }
+ _olm_ed25519_public_key verify_key;
+ olm::decode_base64(from_c(key), key_length, verify_key.public_key);
+ std::size_t raw_signature_length = b64_input(
+ from_c(signature), signature_length, from_c(utility)->last_error
+ );
+ if (raw_signature_length == std::size_t(-1)) {
+ return std::size_t(-1);
+ }
+ return from_c(utility)->ed25519_verify(
+ verify_key,
+ from_c(message), message_length,
+ from_c(signature), raw_signature_length
+ );
+}
+
+}
--- /dev/null
+/* Copyright 2016 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "olm/outbound_group_session.h"
+
+#include <string.h>
+
+#include "olm/base64.h"
+#include "olm/cipher.h"
+#include "olm/crypto.h"
+#include "olm/error.h"
+#include "olm/megolm.h"
+#include "olm/memory.h"
+#include "olm/message.h"
+#include "olm/pickle.h"
+#include "olm/pickle_encoding.h"
+
+#define OLM_PROTOCOL_VERSION 3
+#define GROUP_SESSION_ID_LENGTH ED25519_PUBLIC_KEY_LENGTH
+#define PICKLE_VERSION 1
+#define SESSION_KEY_VERSION 2
+
+struct OlmOutboundGroupSession {
+ /** the Megolm ratchet providing the encryption keys */
+ Megolm ratchet;
+
+ /** The ed25519 keypair used for signing the messages */
+ struct _olm_ed25519_key_pair signing_key;
+
+ enum OlmErrorCode last_error;
+};
+
+
+size_t olm_outbound_group_session_size(void) {
+ return sizeof(OlmOutboundGroupSession);
+}
+
+OlmOutboundGroupSession * olm_outbound_group_session(
+ void *memory
+) {
+ OlmOutboundGroupSession *session = memory;
+ olm_clear_outbound_group_session(session);
+ return session;
+}
+
+const char *olm_outbound_group_session_last_error(
+ const OlmOutboundGroupSession *session
+) {
+ return _olm_error_to_string(session->last_error);
+}
+
+enum OlmErrorCode olm_outbound_group_session_last_error_code(
+ const OlmOutboundGroupSession *session
+) {
+ return session->last_error;
+}
+
+size_t olm_clear_outbound_group_session(
+ OlmOutboundGroupSession *session
+) {
+ _olm_unset(session, sizeof(OlmOutboundGroupSession));
+ return sizeof(OlmOutboundGroupSession);
+}
+
+static size_t raw_pickle_length(
+ const OlmOutboundGroupSession *session
+) {
+ size_t length = 0;
+ length += _olm_pickle_uint32_length(PICKLE_VERSION);
+ length += megolm_pickle_length(&(session->ratchet));
+ length += _olm_pickle_ed25519_key_pair_length(&(session->signing_key));
+ return length;
+}
+
+size_t olm_pickle_outbound_group_session_length(
+ const OlmOutboundGroupSession *session
+) {
+ return _olm_enc_output_length(raw_pickle_length(session));
+}
+
+size_t olm_pickle_outbound_group_session(
+ OlmOutboundGroupSession *session,
+ void const * key, size_t key_length,
+ void * pickled, size_t pickled_length
+) {
+ size_t raw_length = raw_pickle_length(session);
+ uint8_t *pos;
+
+ if (pickled_length < _olm_enc_output_length(raw_length)) {
+ session->last_error = OLM_OUTPUT_BUFFER_TOO_SMALL;
+ return (size_t)-1;
+ }
+
+#ifndef OLM_FUZZING
+ pos = _olm_enc_output_pos(pickled, raw_length);
+#else
+ pos = pickled;
+#endif
+
+ pos = _olm_pickle_uint32(pos, PICKLE_VERSION);
+ pos = megolm_pickle(&(session->ratchet), pos);
+ pos = _olm_pickle_ed25519_key_pair(pos, &(session->signing_key));
+
+#ifndef OLM_FUZZING
+ return _olm_enc_output(key, key_length, pickled, raw_length);
+#else
+ return raw_length;
+#endif
+}
+
+size_t olm_unpickle_outbound_group_session(
+ OlmOutboundGroupSession *session,
+ void const * key, size_t key_length,
+ void * pickled, size_t pickled_length
+) {
+ const uint8_t *pos;
+ const uint8_t *end;
+ uint32_t pickle_version;
+
+#ifndef OLM_FUZZING
+ size_t raw_length = _olm_enc_input(
+ key, key_length, pickled, pickled_length, &(session->last_error)
+ );
+#else
+ size_t raw_length = pickled_length;
+#endif
+
+ if (raw_length == (size_t)-1) {
+ return raw_length;
+ }
+
+ pos = pickled;
+ end = pos + raw_length;
+
+ pos = _olm_unpickle_uint32(pos, end, &pickle_version);
+ FAIL_ON_CORRUPTED_PICKLE(pos, session);
+
+ if (pickle_version != PICKLE_VERSION) {
+ session->last_error = OLM_UNKNOWN_PICKLE_VERSION;
+ return (size_t)-1;
+ }
+
+ pos = megolm_unpickle(&(session->ratchet), pos, end);
+ FAIL_ON_CORRUPTED_PICKLE(pos, session);
+
+ pos = _olm_unpickle_ed25519_key_pair(pos, end, &(session->signing_key));
+ FAIL_ON_CORRUPTED_PICKLE(pos, session);
+
+ if (pos != end) {
+ /* Input was longer than expected. */
+ session->last_error = OLM_PICKLE_EXTRA_DATA;
+ return (size_t)-1;
+ }
+
+ return pickled_length;
+}
+
+
+size_t olm_init_outbound_group_session_random_length(
+ const OlmOutboundGroupSession *session
+) {
+ /* we need data to initialize the megolm ratchet, plus some more for the
+ * session id.
+ */
+ return MEGOLM_RATCHET_LENGTH +
+ ED25519_RANDOM_LENGTH;
+}
+
+size_t olm_init_outbound_group_session(
+ OlmOutboundGroupSession *session,
+ uint8_t *random, size_t random_length
+) {
+ const uint8_t *random_ptr = random;
+
+ if (random_length < olm_init_outbound_group_session_random_length(session)) {
+ /* Insufficient random data for new session */
+ session->last_error = OLM_NOT_ENOUGH_RANDOM;
+ return (size_t)-1;
+ }
+
+ megolm_init(&(session->ratchet), random_ptr, 0);
+ random_ptr += MEGOLM_RATCHET_LENGTH;
+
+ _olm_crypto_ed25519_generate_key(random_ptr, &(session->signing_key));
+ random_ptr += ED25519_RANDOM_LENGTH;
+
+ _olm_unset(random, random_length);
+ return 0;
+}
+
+static size_t raw_message_length(
+ OlmOutboundGroupSession *session,
+ size_t plaintext_length)
+{
+ size_t ciphertext_length, mac_length;
+
+ ciphertext_length = megolm_cipher->ops->encrypt_ciphertext_length(
+ megolm_cipher, plaintext_length
+ );
+
+ mac_length = megolm_cipher->ops->mac_length(megolm_cipher);
+
+ return _olm_encode_group_message_length(
+ session->ratchet.counter,
+ ciphertext_length, mac_length, ED25519_SIGNATURE_LENGTH
+ );
+}
+
+size_t olm_group_encrypt_message_length(
+ OlmOutboundGroupSession *session,
+ size_t plaintext_length
+) {
+ size_t message_length = raw_message_length(session, plaintext_length);
+ return _olm_encode_base64_length(message_length);
+}
+
+/** write an un-base64-ed message to the buffer */
+static size_t _encrypt(
+ OlmOutboundGroupSession *session, uint8_t const * plaintext, size_t plaintext_length,
+ uint8_t * buffer
+) {
+ size_t ciphertext_length, mac_length, message_length;
+ size_t result;
+ uint8_t *ciphertext_ptr;
+
+ ciphertext_length = megolm_cipher->ops->encrypt_ciphertext_length(
+ megolm_cipher,
+ plaintext_length
+ );
+
+ mac_length = megolm_cipher->ops->mac_length(megolm_cipher);
+
+ /* first we build the message structure, then we encrypt
+ * the plaintext into it.
+ */
+ message_length = _olm_encode_group_message(
+ OLM_PROTOCOL_VERSION,
+ session->ratchet.counter,
+ ciphertext_length,
+ buffer,
+ &ciphertext_ptr);
+
+ message_length += mac_length;
+
+ result = megolm_cipher->ops->encrypt(
+ megolm_cipher,
+ megolm_get_data(&(session->ratchet)), MEGOLM_RATCHET_LENGTH,
+ plaintext, plaintext_length,
+ ciphertext_ptr, ciphertext_length,
+ buffer, message_length
+ );
+
+ if (result == (size_t)-1) {
+ return result;
+ }
+
+ megolm_advance(&(session->ratchet));
+
+ /* sign the whole thing with the ed25519 key. */
+ _olm_crypto_ed25519_sign(
+ &(session->signing_key),
+ buffer, message_length,
+ buffer + message_length
+ );
+
+ return result;
+}
+
+size_t olm_group_encrypt(
+ OlmOutboundGroupSession *session,
+ uint8_t const * plaintext, size_t plaintext_length,
+ uint8_t * message, size_t max_message_length
+) {
+ size_t rawmsglen;
+ size_t result;
+ uint8_t *message_pos;
+
+ rawmsglen = raw_message_length(session, plaintext_length);
+
+ if (max_message_length < _olm_encode_base64_length(rawmsglen)) {
+ session->last_error = OLM_OUTPUT_BUFFER_TOO_SMALL;
+ return (size_t)-1;
+ }
+
+ /* we construct the message at the end of the buffer, so that
+ * we have room to base64-encode it once we're done.
+ */
+ message_pos = message + _olm_encode_base64_length(rawmsglen) - rawmsglen;
+
+ /* write the message, and encrypt it, at message_pos */
+ result = _encrypt(session, plaintext, plaintext_length, message_pos);
+ if (result == (size_t)-1) {
+ return result;
+ }
+
+ /* bas64-encode it */
+ return _olm_encode_base64(
+ message_pos, rawmsglen, message
+ );
+}
+
+
+size_t olm_outbound_group_session_id_length(
+ const OlmOutboundGroupSession *session
+) {
+ return _olm_encode_base64_length(GROUP_SESSION_ID_LENGTH);
+}
+
+size_t olm_outbound_group_session_id(
+ OlmOutboundGroupSession *session,
+ uint8_t * id, size_t id_length
+) {
+ if (id_length < olm_outbound_group_session_id_length(session)) {
+ session->last_error = OLM_OUTPUT_BUFFER_TOO_SMALL;
+ return (size_t)-1;
+ }
+
+ return _olm_encode_base64(
+ session->signing_key.public_key.public_key, GROUP_SESSION_ID_LENGTH, id
+ );
+}
+
+uint32_t olm_outbound_group_session_message_index(
+ OlmOutboundGroupSession *session
+) {
+ return session->ratchet.counter;
+}
+
+#define SESSION_KEY_RAW_LENGTH \
+ (1 + 4 + MEGOLM_RATCHET_LENGTH + ED25519_PUBLIC_KEY_LENGTH\
+ + ED25519_SIGNATURE_LENGTH)
+
+size_t olm_outbound_group_session_key_length(
+ const OlmOutboundGroupSession *session
+) {
+ return _olm_encode_base64_length(SESSION_KEY_RAW_LENGTH);
+}
+
+size_t olm_outbound_group_session_key(
+ OlmOutboundGroupSession *session,
+ uint8_t * key, size_t key_length
+) {
+ uint8_t *raw;
+ uint8_t *ptr;
+ size_t encoded_length = olm_outbound_group_session_key_length(session);
+
+ if (key_length < encoded_length) {
+ session->last_error = OLM_OUTPUT_BUFFER_TOO_SMALL;
+ return (size_t)-1;
+ }
+
+ /* put the raw data at the end of the output buffer. */
+ raw = ptr = key + encoded_length - SESSION_KEY_RAW_LENGTH;
+ *ptr++ = SESSION_KEY_VERSION;
+
+ uint32_t counter = session->ratchet.counter;
+ // Encode counter as a big endian 32-bit number.
+ for (unsigned i = 0; i < 4; i++) {
+ *ptr++ = 0xFF & (counter >> 24); counter <<= 8;
+ }
+
+ memcpy(ptr, megolm_get_data(&session->ratchet), MEGOLM_RATCHET_LENGTH);
+ ptr += MEGOLM_RATCHET_LENGTH;
+
+ memcpy(
+ ptr, session->signing_key.public_key.public_key,
+ ED25519_PUBLIC_KEY_LENGTH
+ );
+ ptr += ED25519_PUBLIC_KEY_LENGTH;
+
+ /* sign the whole thing with the ed25519 key. */
+ _olm_crypto_ed25519_sign(
+ &(session->signing_key),
+ raw, ptr - raw, ptr
+ );
+
+ return _olm_encode_base64(raw, SESSION_KEY_RAW_LENGTH, key);
+}
--- /dev/null
+/* Copyright 2015 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "olm/pickle.hh"
+#include "olm/pickle.h"
+
+std::uint8_t * olm::pickle(
+ std::uint8_t * pos,
+ std::uint32_t value
+) {
+ pos += 4;
+ for (unsigned i = 4; i--;) { *(--pos) = value; value >>= 8; }
+ return pos + 4;
+}
+
+
+std::uint8_t const * olm::unpickle(
+ std::uint8_t const * pos, std::uint8_t const * end,
+ std::uint32_t & value
+) {
+ value = 0;
+ if (!pos || end < pos + 4) return nullptr;
+ for (unsigned i = 4; i--;) { value <<= 8; value |= *(pos++); }
+ return pos;
+}
+
+std::uint8_t * olm::pickle(
+ std::uint8_t * pos,
+ std::uint8_t value
+) {
+ *(pos++) = value;
+ return pos;
+}
+
+std::uint8_t const * olm::unpickle(
+ std::uint8_t const * pos, std::uint8_t const * end,
+ std::uint8_t & value
+) {
+ if (!pos || pos == end) return nullptr;
+ value = *(pos++);
+ return pos;
+}
+
+std::uint8_t * olm::pickle(
+ std::uint8_t * pos,
+ bool value
+) {
+ *(pos++) = value ? 1 : 0;
+ return pos;
+}
+
+std::uint8_t const * olm::unpickle(
+ std::uint8_t const * pos, std::uint8_t const * end,
+ bool & value
+) {
+ if (!pos || pos == end) return nullptr;
+ value = *(pos++);
+ return pos;
+}
+
+std::uint8_t * olm::pickle_bytes(
+ std::uint8_t * pos,
+ std::uint8_t const * bytes, std::size_t bytes_length
+) {
+ std::memcpy(pos, bytes, bytes_length);
+ return pos + bytes_length;
+}
+
+std::uint8_t const * olm::unpickle_bytes(
+ std::uint8_t const * pos, std::uint8_t const * end,
+ std::uint8_t * bytes, std::size_t bytes_length
+) {
+ if (!pos || end < pos + bytes_length) return nullptr;
+ std::memcpy(bytes, pos, bytes_length);
+ return pos + bytes_length;
+}
+
+
+std::size_t olm::pickle_length(
+ const _olm_curve25519_public_key & value
+) {
+ return sizeof(value.public_key);
+}
+
+
+std::uint8_t * olm::pickle(
+ std::uint8_t * pos,
+ const _olm_curve25519_public_key & value
+) {
+ pos = olm::pickle_bytes(
+ pos, value.public_key, sizeof(value.public_key)
+ );
+ return pos;
+}
+
+
+std::uint8_t const * olm::unpickle(
+ std::uint8_t const * pos, std::uint8_t const * end,
+ _olm_curve25519_public_key & value
+) {
+ return olm::unpickle_bytes(
+ pos, end, value.public_key, sizeof(value.public_key)
+ );
+}
+
+
+std::size_t olm::pickle_length(
+ const _olm_curve25519_key_pair & value
+) {
+ return sizeof(value.public_key.public_key)
+ + sizeof(value.private_key.private_key);
+}
+
+
+std::uint8_t * olm::pickle(
+ std::uint8_t * pos,
+ const _olm_curve25519_key_pair & value
+) {
+ pos = olm::pickle_bytes(
+ pos, value.public_key.public_key,
+ sizeof(value.public_key.public_key)
+ );
+ pos = olm::pickle_bytes(
+ pos, value.private_key.private_key,
+ sizeof(value.private_key.private_key)
+ );
+ return pos;
+}
+
+
+std::uint8_t const * olm::unpickle(
+ std::uint8_t const * pos, std::uint8_t const * end,
+ _olm_curve25519_key_pair & value
+) {
+ pos = olm::unpickle_bytes(
+ pos, end, value.public_key.public_key,
+ sizeof(value.public_key.public_key)
+ );
+ if (!pos) return nullptr;
+
+ pos = olm::unpickle_bytes(
+ pos, end, value.private_key.private_key,
+ sizeof(value.private_key.private_key)
+ );
+ if (!pos) return nullptr;
+
+ return pos;
+}
+
+////// pickle.h implementations
+
+std::size_t _olm_pickle_ed25519_public_key_length(
+ const _olm_ed25519_public_key * value
+) {
+ return sizeof(value->public_key);
+}
+
+
+std::uint8_t * _olm_pickle_ed25519_public_key(
+ std::uint8_t * pos,
+ const _olm_ed25519_public_key *value
+) {
+ return olm::pickle_bytes(
+ pos, value->public_key, sizeof(value->public_key)
+ );
+}
+
+
+std::uint8_t const * _olm_unpickle_ed25519_public_key(
+ std::uint8_t const * pos, std::uint8_t const * end,
+ _olm_ed25519_public_key * value
+) {
+ return olm::unpickle_bytes(
+ pos, end, value->public_key, sizeof(value->public_key)
+ );
+}
+
+
+std::size_t _olm_pickle_ed25519_key_pair_length(
+ const _olm_ed25519_key_pair *value
+) {
+ return sizeof(value->public_key.public_key)
+ + sizeof(value->private_key.private_key);
+}
+
+
+std::uint8_t * _olm_pickle_ed25519_key_pair(
+ std::uint8_t * pos,
+ const _olm_ed25519_key_pair *value
+) {
+ pos = olm::pickle_bytes(
+ pos, value->public_key.public_key,
+ sizeof(value->public_key.public_key)
+ );
+ pos = olm::pickle_bytes(
+ pos, value->private_key.private_key,
+ sizeof(value->private_key.private_key)
+ );
+ return pos;
+}
+
+
+std::uint8_t const * _olm_unpickle_ed25519_key_pair(
+ std::uint8_t const * pos, std::uint8_t const * end,
+ _olm_ed25519_key_pair *value
+) {
+ pos = olm::unpickle_bytes(
+ pos, end, value->public_key.public_key,
+ sizeof(value->public_key.public_key)
+ );
+ if (!pos) return nullptr;
+
+ pos = olm::unpickle_bytes(
+ pos, end, value->private_key.private_key,
+ sizeof(value->private_key.private_key)
+ );
+ if (!pos) return nullptr;
+
+ return pos;
+}
+
+uint8_t * _olm_pickle_uint32(uint8_t * pos, uint32_t value) {
+ return olm::pickle(pos, value);
+}
+
+uint8_t const * _olm_unpickle_uint32(
+ uint8_t const * pos, uint8_t const * end,
+ uint32_t *value
+) {
+ return olm::unpickle(pos, end, *value);
+}
+
+uint8_t * _olm_pickle_uint8(uint8_t * pos, uint8_t value) {
+ return olm::pickle(pos, value);
+}
+
+uint8_t const * _olm_unpickle_uint8(
+ uint8_t const * pos, uint8_t const * end,
+ uint8_t *value
+) {
+ return olm::unpickle(pos, end, *value);
+}
+
+uint8_t * _olm_pickle_bool(uint8_t * pos, int value) {
+ return olm::pickle(pos, (bool)value);
+}
+
+uint8_t const * _olm_unpickle_bool(
+ uint8_t const * pos, uint8_t const * end,
+ int *value
+) {
+ return olm::unpickle(pos, end, *reinterpret_cast<bool *>(value));
+}
+
+uint8_t * _olm_pickle_bytes(uint8_t * pos, uint8_t const * bytes,
+ size_t bytes_length) {
+ return olm::pickle_bytes(pos, bytes, bytes_length);
+}
+
+uint8_t const * _olm_unpickle_bytes(uint8_t const * pos, uint8_t const * end,
+ uint8_t * bytes, size_t bytes_length) {
+ return olm::unpickle_bytes(pos, end, bytes, bytes_length);
+}
--- /dev/null
+/* Copyright 2016 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "olm/pickle_encoding.h"
+
+#include "olm/base64.h"
+#include "olm/cipher.h"
+#include "olm/olm.h"
+
+static const struct _olm_cipher_aes_sha_256 PICKLE_CIPHER =
+ OLM_CIPHER_INIT_AES_SHA_256("Pickle");
+
+size_t _olm_enc_output_length(
+ size_t raw_length
+) {
+ const struct _olm_cipher *cipher = OLM_CIPHER_BASE(&PICKLE_CIPHER);
+ size_t length = cipher->ops->encrypt_ciphertext_length(cipher, raw_length);
+ length += cipher->ops->mac_length(cipher);
+ return _olm_encode_base64_length(length);
+}
+
+uint8_t * _olm_enc_output_pos(
+ uint8_t * output,
+ size_t raw_length
+) {
+ const struct _olm_cipher *cipher = OLM_CIPHER_BASE(&PICKLE_CIPHER);
+ size_t length = cipher->ops->encrypt_ciphertext_length(cipher, raw_length);
+ length += cipher->ops->mac_length(cipher);
+ return output + _olm_encode_base64_length(length) - length;
+}
+
+size_t _olm_enc_output(
+ uint8_t const * key, size_t key_length,
+ uint8_t * output, size_t raw_length
+) {
+ const struct _olm_cipher *cipher = OLM_CIPHER_BASE(&PICKLE_CIPHER);
+ size_t ciphertext_length = cipher->ops->encrypt_ciphertext_length(
+ cipher, raw_length
+ );
+ size_t length = ciphertext_length + cipher->ops->mac_length(cipher);
+ size_t base64_length = _olm_encode_base64_length(length);
+ uint8_t * raw_output = output + base64_length - length;
+ cipher->ops->encrypt(
+ cipher,
+ key, key_length,
+ raw_output, raw_length,
+ raw_output, ciphertext_length,
+ raw_output, length
+ );
+ _olm_encode_base64(raw_output, length, output);
+ return base64_length;
+}
+
+
+size_t _olm_enc_input(uint8_t const * key, size_t key_length,
+ uint8_t * input, size_t b64_length,
+ enum OlmErrorCode * last_error
+) {
+ size_t enc_length = _olm_decode_base64_length(b64_length);
+ if (enc_length == (size_t)-1) {
+ if (last_error) {
+ *last_error = OLM_INVALID_BASE64;
+ }
+ return (size_t)-1;
+ }
+ _olm_decode_base64(input, b64_length, input);
+ const struct _olm_cipher *cipher = OLM_CIPHER_BASE(&PICKLE_CIPHER);
+ size_t raw_length = enc_length - cipher->ops->mac_length(cipher);
+ size_t result = cipher->ops->decrypt(
+ cipher,
+ key, key_length,
+ input, enc_length,
+ input, raw_length,
+ input, raw_length
+ );
+ if (result == (size_t)-1 && last_error) {
+ *last_error = OLM_BAD_ACCOUNT_KEY;
+ }
+ return result;
+}
--- /dev/null
+/* Copyright 2018, 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "olm/pk.h"
+#include "olm/cipher.h"
+#include "olm/crypto.h"
+#include "olm/ratchet.hh"
+#include "olm/error.h"
+#include "olm/memory.hh"
+#include "olm/base64.hh"
+#include "olm/pickle_encoding.h"
+#include "olm/pickle.hh"
+
+static const std::size_t MAC_LENGTH = 8;
+
+const struct _olm_cipher_aes_sha_256 olm_pk_cipher_aes_sha256 =
+ OLM_CIPHER_INIT_AES_SHA_256("");
+const struct _olm_cipher *olm_pk_cipher =
+ OLM_CIPHER_BASE(&olm_pk_cipher_aes_sha256);
+
+extern "C" {
+
+struct OlmPkEncryption {
+ OlmErrorCode last_error;
+ _olm_curve25519_public_key recipient_key;
+};
+
+const char * olm_pk_encryption_last_error(
+ const OlmPkEncryption * encryption
+) {
+ auto error = encryption->last_error;
+ return _olm_error_to_string(error);
+}
+
+OlmErrorCode olm_pk_encryption_last_error_code(
+ const OlmPkEncryption * encryption
+) {
+ return encryption->last_error;
+}
+
+size_t olm_pk_encryption_size(void) {
+ return sizeof(OlmPkEncryption);
+}
+
+OlmPkEncryption *olm_pk_encryption(
+ void * memory
+) {
+ olm::unset(memory, sizeof(OlmPkEncryption));
+ return new(memory) OlmPkEncryption;
+}
+
+size_t olm_clear_pk_encryption(
+ OlmPkEncryption *encryption
+) {
+ /* Clear the memory backing the encryption */
+ olm::unset(encryption, sizeof(OlmPkEncryption));
+ /* Initialise a fresh encryption object in case someone tries to use it */
+ new(encryption) OlmPkEncryption();
+ return sizeof(OlmPkEncryption);
+}
+
+size_t olm_pk_encryption_set_recipient_key (
+ OlmPkEncryption *encryption,
+ void const * key, size_t key_length
+) {
+ if (key_length < olm_pk_key_length()) {
+ encryption->last_error =
+ OlmErrorCode::OLM_INPUT_BUFFER_TOO_SMALL;
+ return std::size_t(-1);
+ }
+
+ olm::decode_base64(
+ (const uint8_t*)key,
+ olm_pk_key_length(),
+ (uint8_t *)encryption->recipient_key.public_key
+ );
+
+ return 0;
+}
+
+size_t olm_pk_ciphertext_length(
+ const OlmPkEncryption *encryption,
+ size_t plaintext_length
+) {
+ return olm::encode_base64_length(
+ _olm_cipher_aes_sha_256_ops.encrypt_ciphertext_length(olm_pk_cipher, plaintext_length)
+ );
+}
+
+size_t olm_pk_mac_length(
+ const OlmPkEncryption *encryption
+) {
+ return olm::encode_base64_length(_olm_cipher_aes_sha_256_ops.mac_length(olm_pk_cipher));
+}
+
+size_t olm_pk_encrypt_random_length(
+ const OlmPkEncryption *encryption
+) {
+ return CURVE25519_KEY_LENGTH;
+}
+
+size_t olm_pk_encrypt(
+ OlmPkEncryption *encryption,
+ void const * plaintext, size_t plaintext_length,
+ void * ciphertext, size_t ciphertext_length,
+ void * mac, size_t mac_length,
+ void * ephemeral_key, size_t ephemeral_key_size,
+ const void * random, size_t random_length
+) {
+ if (ciphertext_length
+ < olm_pk_ciphertext_length(encryption, plaintext_length)
+ || mac_length
+ < _olm_cipher_aes_sha_256_ops.mac_length(olm_pk_cipher)
+ || ephemeral_key_size
+ < olm_pk_key_length()) {
+ encryption->last_error =
+ OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
+ return std::size_t(-1);
+ }
+ if (random_length < olm_pk_encrypt_random_length(encryption)) {
+ encryption->last_error =
+ OlmErrorCode::OLM_NOT_ENOUGH_RANDOM;
+ return std::size_t(-1);
+ }
+
+ _olm_curve25519_key_pair ephemeral_keypair;
+ _olm_crypto_curve25519_generate_key((const uint8_t *) random, &ephemeral_keypair);
+ olm::encode_base64(
+ (const uint8_t *)ephemeral_keypair.public_key.public_key,
+ CURVE25519_KEY_LENGTH,
+ (uint8_t *)ephemeral_key
+ );
+
+ olm::SharedKey secret;
+ _olm_crypto_curve25519_shared_secret(&ephemeral_keypair, &encryption->recipient_key, secret);
+ size_t raw_ciphertext_length =
+ _olm_cipher_aes_sha_256_ops.encrypt_ciphertext_length(olm_pk_cipher, plaintext_length);
+ uint8_t *ciphertext_pos = (uint8_t *) ciphertext + ciphertext_length - raw_ciphertext_length;
+ uint8_t raw_mac[MAC_LENGTH];
+ size_t result = _olm_cipher_aes_sha_256_ops.encrypt(
+ olm_pk_cipher,
+ secret, sizeof(secret),
+ (const uint8_t *) plaintext, plaintext_length,
+ (uint8_t *) ciphertext_pos, raw_ciphertext_length,
+ (uint8_t *) raw_mac, MAC_LENGTH
+ );
+ if (result != std::size_t(-1)) {
+ olm::encode_base64(raw_mac, MAC_LENGTH, (uint8_t *)mac);
+ olm::encode_base64(ciphertext_pos, raw_ciphertext_length, (uint8_t *)ciphertext);
+ }
+ return result;
+}
+
+struct OlmPkDecryption {
+ OlmErrorCode last_error;
+ _olm_curve25519_key_pair key_pair;
+};
+
+const char * olm_pk_decryption_last_error(
+ const OlmPkDecryption * decryption
+) {
+ auto error = decryption->last_error;
+ return _olm_error_to_string(error);
+}
+
+OlmErrorCode olm_pk_decryption_last_error_code(
+ const OlmPkDecryption * decryption
+) {
+ return decryption->last_error;
+}
+
+size_t olm_pk_decryption_size(void) {
+ return sizeof(OlmPkDecryption);
+}
+
+OlmPkDecryption *olm_pk_decryption(
+ void * memory
+) {
+ olm::unset(memory, sizeof(OlmPkDecryption));
+ return new(memory) OlmPkDecryption;
+}
+
+size_t olm_clear_pk_decryption(
+ OlmPkDecryption *decryption
+) {
+ /* Clear the memory backing the decryption */
+ olm::unset(decryption, sizeof(OlmPkDecryption));
+ /* Initialise a fresh decryption object in case someone tries to use it */
+ new(decryption) OlmPkDecryption();
+ return sizeof(OlmPkDecryption);
+}
+
+size_t olm_pk_private_key_length(void) {
+ return CURVE25519_KEY_LENGTH;
+}
+
+size_t olm_pk_generate_key_random_length(void) {
+ return olm_pk_private_key_length();
+}
+
+size_t olm_pk_key_length(void) {
+ return olm::encode_base64_length(CURVE25519_KEY_LENGTH);
+}
+
+size_t olm_pk_key_from_private(
+ OlmPkDecryption * decryption,
+ void * pubkey, size_t pubkey_length,
+ const void * privkey, size_t privkey_length
+) {
+ if (pubkey_length < olm_pk_key_length()) {
+ decryption->last_error =
+ OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
+ return std::size_t(-1);
+ }
+ if (privkey_length < olm_pk_private_key_length()) {
+ decryption->last_error =
+ OlmErrorCode::OLM_INPUT_BUFFER_TOO_SMALL;
+ return std::size_t(-1);
+ }
+
+ _olm_crypto_curve25519_generate_key((const uint8_t *) privkey, &decryption->key_pair);
+ olm::encode_base64(
+ (const uint8_t *)decryption->key_pair.public_key.public_key,
+ CURVE25519_KEY_LENGTH,
+ (uint8_t *)pubkey
+ );
+ return 0;
+}
+
+size_t olm_pk_generate_key(
+ OlmPkDecryption * decryption,
+ void * pubkey, size_t pubkey_length,
+ const void * privkey, size_t privkey_length
+) {
+ return olm_pk_key_from_private(decryption, pubkey, pubkey_length, privkey, privkey_length);
+}
+
+namespace {
+ static const std::uint32_t PK_DECRYPTION_PICKLE_VERSION = 1;
+
+ static std::size_t pickle_length(
+ OlmPkDecryption const & value
+ ) {
+ std::size_t length = 0;
+ length += olm::pickle_length(PK_DECRYPTION_PICKLE_VERSION);
+ length += olm::pickle_length(value.key_pair);
+ return length;
+ }
+
+
+ static std::uint8_t * pickle(
+ std::uint8_t * pos,
+ OlmPkDecryption const & value
+ ) {
+ pos = olm::pickle(pos, PK_DECRYPTION_PICKLE_VERSION);
+ pos = olm::pickle(pos, value.key_pair);
+ return pos;
+ }
+
+
+ static std::uint8_t const * unpickle(
+ std::uint8_t const * pos, std::uint8_t const * end,
+ OlmPkDecryption & value
+ ) {
+ uint32_t pickle_version;
+ pos = olm::unpickle(pos, end, pickle_version); UNPICKLE_OK(pos);
+
+ switch (pickle_version) {
+ case 1:
+ break;
+
+ default:
+ value.last_error = OlmErrorCode::OLM_UNKNOWN_PICKLE_VERSION;
+ return nullptr;
+ }
+
+ pos = olm::unpickle(pos, end, value.key_pair); UNPICKLE_OK(pos);
+
+ return pos;
+ }
+}
+
+size_t olm_pickle_pk_decryption_length(
+ const OlmPkDecryption * decryption
+) {
+ return _olm_enc_output_length(pickle_length(*decryption));
+}
+
+size_t olm_pickle_pk_decryption(
+ OlmPkDecryption * decryption,
+ void const * key, size_t key_length,
+ void *pickled, size_t pickled_length
+) {
+ OlmPkDecryption & object = *decryption;
+ std::size_t raw_length = pickle_length(object);
+ if (pickled_length < _olm_enc_output_length(raw_length)) {
+ object.last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
+ return std::size_t(-1);
+ }
+ pickle(_olm_enc_output_pos(reinterpret_cast<std::uint8_t *>(pickled), raw_length), object);
+ return _olm_enc_output(
+ reinterpret_cast<std::uint8_t const *>(key), key_length,
+ reinterpret_cast<std::uint8_t *>(pickled), raw_length
+ );
+}
+
+size_t olm_unpickle_pk_decryption(
+ OlmPkDecryption * decryption,
+ void const * key, size_t key_length,
+ void *pickled, size_t pickled_length,
+ void *pubkey, size_t pubkey_length
+) {
+ OlmPkDecryption & object = *decryption;
+ if (pubkey != NULL && pubkey_length < olm_pk_key_length()) {
+ object.last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
+ return std::size_t(-1);
+ }
+ std::uint8_t * const input = reinterpret_cast<std::uint8_t *>(pickled);
+ std::size_t raw_length = _olm_enc_input(
+ reinterpret_cast<std::uint8_t const *>(key), key_length,
+ input, pickled_length, &object.last_error
+ );
+ if (raw_length == std::size_t(-1)) {
+ return std::size_t(-1);
+ }
+
+ std::uint8_t const * pos = input;
+ std::uint8_t const * end = pos + raw_length;
+
+ pos = unpickle(pos, end, object);
+
+ if (!pos) {
+ /* Input was corrupted. */
+ if (object.last_error == OlmErrorCode::OLM_SUCCESS) {
+ object.last_error = OlmErrorCode::OLM_CORRUPTED_PICKLE;
+ }
+ return std::size_t(-1);
+ } else if (pos != end) {
+ /* Input was longer than expected. */
+ object.last_error = OlmErrorCode::OLM_PICKLE_EXTRA_DATA;
+ return std::size_t(-1);
+ }
+
+ if (pubkey != NULL) {
+ olm::encode_base64(
+ (const uint8_t *)object.key_pair.public_key.public_key,
+ CURVE25519_KEY_LENGTH,
+ (uint8_t *)pubkey
+ );
+ }
+
+ return pickled_length;
+}
+
+size_t olm_pk_max_plaintext_length(
+ const OlmPkDecryption * decryption,
+ size_t ciphertext_length
+) {
+ return _olm_cipher_aes_sha_256_ops.decrypt_max_plaintext_length(
+ olm_pk_cipher, olm::decode_base64_length(ciphertext_length)
+ );
+}
+
+size_t olm_pk_decrypt(
+ OlmPkDecryption * decryption,
+ void const * ephemeral_key, size_t ephemeral_key_length,
+ void const * mac, size_t mac_length,
+ void * ciphertext, size_t ciphertext_length,
+ void * plaintext, size_t max_plaintext_length
+) {
+ if (max_plaintext_length
+ < olm_pk_max_plaintext_length(decryption, ciphertext_length)) {
+ decryption->last_error =
+ OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
+ return std::size_t(-1);
+ }
+
+ size_t raw_ciphertext_length = olm::decode_base64_length(ciphertext_length);
+
+ if (ephemeral_key_length != olm::encode_base64_length(CURVE25519_KEY_LENGTH)
+ || mac_length != olm::encode_base64_length(MAC_LENGTH)
+ || raw_ciphertext_length == std::size_t(-1)) {
+ decryption->last_error = OlmErrorCode::OLM_INVALID_BASE64;
+ return std::size_t(-1);
+ }
+
+ struct _olm_curve25519_public_key ephemeral;
+ olm::decode_base64(
+ (const uint8_t*)ephemeral_key,
+ olm::encode_base64_length(CURVE25519_KEY_LENGTH),
+ (uint8_t *)ephemeral.public_key
+ );
+
+ olm::SharedKey secret;
+ _olm_crypto_curve25519_shared_secret(&decryption->key_pair, &ephemeral, secret);
+
+ uint8_t raw_mac[MAC_LENGTH];
+ olm::decode_base64(
+ (const uint8_t *)mac,
+ olm::encode_base64_length(MAC_LENGTH),
+ raw_mac
+ );
+
+ olm::decode_base64(
+ (const uint8_t *)ciphertext,
+ ciphertext_length,
+ (uint8_t *)ciphertext
+ );
+
+ size_t result = _olm_cipher_aes_sha_256_ops.decrypt(
+ olm_pk_cipher,
+ secret, sizeof(secret),
+ (uint8_t *) raw_mac, MAC_LENGTH,
+ (const uint8_t *) ciphertext, raw_ciphertext_length,
+ (uint8_t *) plaintext, max_plaintext_length
+ );
+ if (result == std::size_t(-1)) {
+ // we already checked the buffer sizes, so the only error that decrypt
+ // will return is if the MAC is incorrect
+ decryption->last_error =
+ OlmErrorCode::OLM_BAD_MESSAGE_MAC;
+ return std::size_t(-1);
+ } else {
+ return result;
+ }
+}
+
+size_t olm_pk_get_private_key(
+ OlmPkDecryption * decryption,
+ void *private_key, size_t private_key_length
+) {
+ if (private_key_length < olm_pk_private_key_length()) {
+ decryption->last_error =
+ OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
+ return std::size_t(-1);
+ }
+ std::memcpy(
+ private_key,
+ decryption->key_pair.private_key.private_key,
+ olm_pk_private_key_length()
+ );
+ return olm_pk_private_key_length();
+}
+
+struct OlmPkSigning {
+ OlmErrorCode last_error;
+ _olm_ed25519_key_pair key_pair;
+};
+
+size_t olm_pk_signing_size(void) {
+ return sizeof(OlmPkSigning);
+}
+
+OlmPkSigning *olm_pk_signing(void * memory) {
+ olm::unset(memory, sizeof(OlmPkSigning));
+ return new(memory) OlmPkSigning;
+}
+
+const char * olm_pk_signing_last_error(const OlmPkSigning * sign) {
+ auto error = sign->last_error;
+ return _olm_error_to_string(error);
+}
+
+OlmErrorCode olm_pk_signing_last_error_code(const OlmPkSigning * sign) {
+ return sign->last_error;
+}
+
+size_t olm_clear_pk_signing(OlmPkSigning *sign) {
+ /* Clear the memory backing the signing */
+ olm::unset(sign, sizeof(OlmPkSigning));
+ /* Initialise a fresh signing object in case someone tries to use it */
+ new(sign) OlmPkSigning();
+ return sizeof(OlmPkSigning);
+}
+
+size_t olm_pk_signing_seed_length(void) {
+ return ED25519_RANDOM_LENGTH;
+}
+
+size_t olm_pk_signing_public_key_length(void) {
+ return olm::encode_base64_length(ED25519_PUBLIC_KEY_LENGTH);
+}
+
+size_t olm_pk_signing_key_from_seed(
+ OlmPkSigning * signing,
+ void * pubkey, size_t pubkey_length,
+ const void * seed, size_t seed_length
+) {
+ if (pubkey_length < olm_pk_signing_public_key_length()) {
+ signing->last_error =
+ OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
+ return std::size_t(-1);
+ }
+ if (seed_length < olm_pk_signing_seed_length()) {
+ signing->last_error =
+ OlmErrorCode::OLM_INPUT_BUFFER_TOO_SMALL;
+ return std::size_t(-1);
+ }
+
+ _olm_crypto_ed25519_generate_key((const uint8_t *) seed, &signing->key_pair);
+ olm::encode_base64(
+ (const uint8_t *)signing->key_pair.public_key.public_key,
+ ED25519_PUBLIC_KEY_LENGTH,
+ (uint8_t *)pubkey
+ );
+ return 0;
+}
+
+size_t olm_pk_signature_length(void) {
+ return olm::encode_base64_length(ED25519_SIGNATURE_LENGTH);
+}
+
+size_t olm_pk_sign(
+ OlmPkSigning *signing,
+ uint8_t const * message, size_t message_length,
+ uint8_t * signature, size_t signature_length
+) {
+ if (signature_length < olm_pk_signature_length()) {
+ signing->last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
+ return std::size_t(-1);
+ }
+ uint8_t *raw_sig = signature + olm_pk_signature_length() - ED25519_SIGNATURE_LENGTH;
+ _olm_crypto_ed25519_sign(
+ &signing->key_pair,
+ message, message_length, raw_sig
+ );
+ olm::encode_base64(raw_sig, ED25519_SIGNATURE_LENGTH, signature);
+ return olm_pk_signature_length();
+}
+
+}
--- /dev/null
+/* Copyright 2015, 2016 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "olm/ratchet.hh"
+#include "olm/message.hh"
+#include "olm/memory.hh"
+#include "olm/cipher.h"
+#include "olm/pickle.hh"
+
+#include <cstring>
+
+namespace {
+
+static const std::uint8_t PROTOCOL_VERSION = 3;
+static const std::uint8_t MESSAGE_KEY_SEED[1] = {0x01};
+static const std::uint8_t CHAIN_KEY_SEED[1] = {0x02};
+static const std::size_t MAX_MESSAGE_GAP = 2000;
+
+
+/**
+ * Advance the root key, creating a new message chain.
+ *
+ * @param root_key previous root key R(n-1)
+ * @param our_key our new ratchet key T(n)
+ * @param their_key their most recent ratchet key T(n-1)
+ * @param info table of constants for the ratchet function
+ * @param new_root_key[out] returns the new root key R(n)
+ * @param new_chain_key[out] returns the first chain key in the new chain
+ * C(n,0)
+ */
+static void create_chain_key(
+ olm::SharedKey const & root_key,
+ _olm_curve25519_key_pair const & our_key,
+ _olm_curve25519_public_key const & their_key,
+ olm::KdfInfo const & info,
+ olm::SharedKey & new_root_key,
+ olm::ChainKey & new_chain_key
+) {
+ olm::SharedKey secret;
+ _olm_crypto_curve25519_shared_secret(&our_key, &their_key, secret);
+ std::uint8_t derived_secrets[2 * olm::OLM_SHARED_KEY_LENGTH];
+ _olm_crypto_hkdf_sha256(
+ secret, sizeof(secret),
+ root_key, sizeof(root_key),
+ info.ratchet_info, info.ratchet_info_length,
+ derived_secrets, sizeof(derived_secrets)
+ );
+ std::uint8_t const * pos = derived_secrets;
+ pos = olm::load_array(new_root_key, pos);
+ pos = olm::load_array(new_chain_key.key, pos);
+ new_chain_key.index = 0;
+ olm::unset(derived_secrets);
+ olm::unset(secret);
+}
+
+
+static void advance_chain_key(
+ olm::ChainKey const & chain_key,
+ olm::ChainKey & new_chain_key
+) {
+ _olm_crypto_hmac_sha256(
+ chain_key.key, sizeof(chain_key.key),
+ CHAIN_KEY_SEED, sizeof(CHAIN_KEY_SEED),
+ new_chain_key.key
+ );
+ new_chain_key.index = chain_key.index + 1;
+}
+
+
+static void create_message_keys(
+ olm::ChainKey const & chain_key,
+ olm::KdfInfo const & info,
+ olm::MessageKey & message_key) {
+ _olm_crypto_hmac_sha256(
+ chain_key.key, sizeof(chain_key.key),
+ MESSAGE_KEY_SEED, sizeof(MESSAGE_KEY_SEED),
+ message_key.key
+ );
+ message_key.index = chain_key.index;
+}
+
+
+static std::size_t verify_mac_and_decrypt(
+ _olm_cipher const *cipher,
+ olm::MessageKey const & message_key,
+ olm::MessageReader const & reader,
+ std::uint8_t * plaintext, std::size_t max_plaintext_length
+) {
+ return cipher->ops->decrypt(
+ cipher,
+ message_key.key, sizeof(message_key.key),
+ reader.input, reader.input_length,
+ reader.ciphertext, reader.ciphertext_length,
+ plaintext, max_plaintext_length
+ );
+}
+
+
+static std::size_t verify_mac_and_decrypt_for_existing_chain(
+ olm::Ratchet const & session,
+ olm::ChainKey const & chain,
+ olm::MessageReader const & reader,
+ std::uint8_t * plaintext, std::size_t max_plaintext_length
+) {
+ if (reader.counter < chain.index) {
+ return std::size_t(-1);
+ }
+
+ /* Limit the number of hashes we're prepared to compute */
+ if (reader.counter - chain.index > MAX_MESSAGE_GAP) {
+ return std::size_t(-1);
+ }
+
+ olm::ChainKey new_chain = chain;
+
+ while (new_chain.index < reader.counter) {
+ advance_chain_key(new_chain, new_chain);
+ }
+
+ olm::MessageKey message_key;
+ create_message_keys(new_chain, session.kdf_info, message_key);
+
+ std::size_t result = verify_mac_and_decrypt(
+ session.ratchet_cipher, message_key, reader,
+ plaintext, max_plaintext_length
+ );
+
+ olm::unset(new_chain);
+ return result;
+}
+
+
+static std::size_t verify_mac_and_decrypt_for_new_chain(
+ olm::Ratchet const & session,
+ olm::MessageReader const & reader,
+ std::uint8_t * plaintext, std::size_t max_plaintext_length
+) {
+ olm::SharedKey new_root_key;
+ olm::ReceiverChain new_chain;
+
+ /* They shouldn't move to a new chain until we've sent them a message
+ * acknowledging the last one */
+ if (session.sender_chain.empty()) {
+ return std::size_t(-1);
+ }
+
+ /* Limit the number of hashes we're prepared to compute */
+ if (reader.counter > MAX_MESSAGE_GAP) {
+ return std::size_t(-1);
+ }
+ olm::load_array(new_chain.ratchet_key.public_key, reader.ratchet_key);
+
+ create_chain_key(
+ session.root_key, session.sender_chain[0].ratchet_key,
+ new_chain.ratchet_key, session.kdf_info,
+ new_root_key, new_chain.chain_key
+ );
+ std::size_t result = verify_mac_and_decrypt_for_existing_chain(
+ session, new_chain.chain_key, reader,
+ plaintext, max_plaintext_length
+ );
+ olm::unset(new_root_key);
+ olm::unset(new_chain);
+ return result;
+}
+
+} // namespace
+
+
+olm::Ratchet::Ratchet(
+ olm::KdfInfo const & kdf_info,
+ _olm_cipher const * ratchet_cipher
+) : kdf_info(kdf_info),
+ ratchet_cipher(ratchet_cipher),
+ last_error(OlmErrorCode::OLM_SUCCESS) {
+}
+
+
+void olm::Ratchet::initialise_as_bob(
+ std::uint8_t const * shared_secret, std::size_t shared_secret_length,
+ _olm_curve25519_public_key const & their_ratchet_key
+) {
+ std::uint8_t derived_secrets[2 * olm::OLM_SHARED_KEY_LENGTH];
+ _olm_crypto_hkdf_sha256(
+ shared_secret, shared_secret_length,
+ nullptr, 0,
+ kdf_info.root_info, kdf_info.root_info_length,
+ derived_secrets, sizeof(derived_secrets)
+ );
+ receiver_chains.insert();
+ receiver_chains[0].chain_key.index = 0;
+ std::uint8_t const * pos = derived_secrets;
+ pos = olm::load_array(root_key, pos);
+ pos = olm::load_array(receiver_chains[0].chain_key.key, pos);
+ receiver_chains[0].ratchet_key = their_ratchet_key;
+ olm::unset(derived_secrets);
+}
+
+
+void olm::Ratchet::initialise_as_alice(
+ std::uint8_t const * shared_secret, std::size_t shared_secret_length,
+ _olm_curve25519_key_pair const & our_ratchet_key
+) {
+ std::uint8_t derived_secrets[2 * olm::OLM_SHARED_KEY_LENGTH];
+ _olm_crypto_hkdf_sha256(
+ shared_secret, shared_secret_length,
+ nullptr, 0,
+ kdf_info.root_info, kdf_info.root_info_length,
+ derived_secrets, sizeof(derived_secrets)
+ );
+ sender_chain.insert();
+ sender_chain[0].chain_key.index = 0;
+ std::uint8_t const * pos = derived_secrets;
+ pos = olm::load_array(root_key, pos);
+ pos = olm::load_array(sender_chain[0].chain_key.key, pos);
+ sender_chain[0].ratchet_key = our_ratchet_key;
+ olm::unset(derived_secrets);
+}
+
+namespace olm {
+
+
+static std::size_t pickle_length(
+ const olm::SharedKey & value
+) {
+ return olm::OLM_SHARED_KEY_LENGTH;
+}
+
+
+static std::uint8_t * pickle(
+ std::uint8_t * pos,
+ const olm::SharedKey & value
+) {
+ return olm::pickle_bytes(pos, value, olm::OLM_SHARED_KEY_LENGTH);
+}
+
+
+static std::uint8_t const * unpickle(
+ std::uint8_t const * pos, std::uint8_t const * end,
+ olm::SharedKey & value
+) {
+ return olm::unpickle_bytes(pos, end, value, olm::OLM_SHARED_KEY_LENGTH);
+}
+
+
+static std::size_t pickle_length(
+ const olm::SenderChain & value
+) {
+ std::size_t length = 0;
+ length += olm::pickle_length(value.ratchet_key);
+ length += olm::pickle_length(value.chain_key.key);
+ length += olm::pickle_length(value.chain_key.index);
+ return length;
+}
+
+
+static std::uint8_t * pickle(
+ std::uint8_t * pos,
+ const olm::SenderChain & value
+) {
+ pos = olm::pickle(pos, value.ratchet_key);
+ pos = olm::pickle(pos, value.chain_key.key);
+ pos = olm::pickle(pos, value.chain_key.index);
+ return pos;
+}
+
+
+static std::uint8_t const * unpickle(
+ std::uint8_t const * pos, std::uint8_t const * end,
+ olm::SenderChain & value
+) {
+ pos = olm::unpickle(pos, end, value.ratchet_key); UNPICKLE_OK(pos);
+ pos = olm::unpickle(pos, end, value.chain_key.key); UNPICKLE_OK(pos);
+ pos = olm::unpickle(pos, end, value.chain_key.index); UNPICKLE_OK(pos);
+ return pos;
+}
+
+static std::size_t pickle_length(
+ const olm::ReceiverChain & value
+) {
+ std::size_t length = 0;
+ length += olm::pickle_length(value.ratchet_key);
+ length += olm::pickle_length(value.chain_key.key);
+ length += olm::pickle_length(value.chain_key.index);
+ return length;
+}
+
+
+static std::uint8_t * pickle(
+ std::uint8_t * pos,
+ const olm::ReceiverChain & value
+) {
+ pos = olm::pickle(pos, value.ratchet_key);
+ pos = olm::pickle(pos, value.chain_key.key);
+ pos = olm::pickle(pos, value.chain_key.index);
+ return pos;
+}
+
+
+static std::uint8_t const * unpickle(
+ std::uint8_t const * pos, std::uint8_t const * end,
+ olm::ReceiverChain & value
+) {
+ pos = olm::unpickle(pos, end, value.ratchet_key); UNPICKLE_OK(pos);
+ pos = olm::unpickle(pos, end, value.chain_key.key); UNPICKLE_OK(pos);
+ pos = olm::unpickle(pos, end, value.chain_key.index); UNPICKLE_OK(pos);
+ return pos;
+}
+
+
+static std::size_t pickle_length(
+ const olm::SkippedMessageKey & value
+) {
+ std::size_t length = 0;
+ length += olm::pickle_length(value.ratchet_key);
+ length += olm::pickle_length(value.message_key.key);
+ length += olm::pickle_length(value.message_key.index);
+ return length;
+}
+
+
+static std::uint8_t * pickle(
+ std::uint8_t * pos,
+ const olm::SkippedMessageKey & value
+) {
+ pos = olm::pickle(pos, value.ratchet_key);
+ pos = olm::pickle(pos, value.message_key.key);
+ pos = olm::pickle(pos, value.message_key.index);
+ return pos;
+}
+
+
+static std::uint8_t const * unpickle(
+ std::uint8_t const * pos, std::uint8_t const * end,
+ olm::SkippedMessageKey & value
+) {
+ pos = olm::unpickle(pos, end, value.ratchet_key); UNPICKLE_OK(pos);
+ pos = olm::unpickle(pos, end, value.message_key.key); UNPICKLE_OK(pos);
+ pos = olm::unpickle(pos, end, value.message_key.index); UNPICKLE_OK(pos);
+ return pos;
+}
+
+
+} // namespace olm
+
+
+std::size_t olm::pickle_length(
+ olm::Ratchet const & value
+) {
+ std::size_t length = 0;
+ length += olm::OLM_SHARED_KEY_LENGTH;
+ length += olm::pickle_length(value.sender_chain);
+ length += olm::pickle_length(value.receiver_chains);
+ length += olm::pickle_length(value.skipped_message_keys);
+ return length;
+}
+
+std::uint8_t * olm::pickle(
+ std::uint8_t * pos,
+ olm::Ratchet const & value
+) {
+ pos = pickle(pos, value.root_key);
+ pos = pickle(pos, value.sender_chain);
+ pos = pickle(pos, value.receiver_chains);
+ pos = pickle(pos, value.skipped_message_keys);
+ return pos;
+}
+
+
+std::uint8_t const * olm::unpickle(
+ std::uint8_t const * pos, std::uint8_t const * end,
+ olm::Ratchet & value,
+ bool includes_chain_index
+) {
+ pos = unpickle(pos, end, value.root_key); UNPICKLE_OK(pos);
+ pos = unpickle(pos, end, value.sender_chain); UNPICKLE_OK(pos);
+ pos = unpickle(pos, end, value.receiver_chains); UNPICKLE_OK(pos);
+ pos = unpickle(pos, end, value.skipped_message_keys); UNPICKLE_OK(pos);
+
+ // pickle v 0x80000001 includes a chain index; pickle v1 does not.
+ if (includes_chain_index) {
+ std::uint32_t dummy;
+ pos = unpickle(pos, end, dummy); UNPICKLE_OK(pos);
+ }
+ return pos;
+}
+
+
+std::size_t olm::Ratchet::encrypt_output_length(
+ std::size_t plaintext_length
+) const {
+ std::size_t counter = 0;
+ if (!sender_chain.empty()) {
+ counter = sender_chain[0].chain_key.index;
+ }
+ std::size_t padded = ratchet_cipher->ops->encrypt_ciphertext_length(
+ ratchet_cipher,
+ plaintext_length
+ );
+ return olm::encode_message_length(
+ counter, CURVE25519_KEY_LENGTH, padded, ratchet_cipher->ops->mac_length(ratchet_cipher)
+ );
+}
+
+
+std::size_t olm::Ratchet::encrypt_random_length() const {
+ return sender_chain.empty() ? CURVE25519_RANDOM_LENGTH : 0;
+}
+
+
+std::size_t olm::Ratchet::encrypt(
+ std::uint8_t const * plaintext, std::size_t plaintext_length,
+ std::uint8_t const * random, std::size_t random_length,
+ std::uint8_t * output, std::size_t max_output_length
+) {
+ std::size_t output_length = encrypt_output_length(plaintext_length);
+
+ if (random_length < encrypt_random_length()) {
+ last_error = OlmErrorCode::OLM_NOT_ENOUGH_RANDOM;
+ return std::size_t(-1);
+ }
+ if (max_output_length < output_length) {
+ last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
+ return std::size_t(-1);
+ }
+
+ if (sender_chain.empty()) {
+ sender_chain.insert();
+ _olm_crypto_curve25519_generate_key(random, &sender_chain[0].ratchet_key);
+ create_chain_key(
+ root_key,
+ sender_chain[0].ratchet_key,
+ receiver_chains[0].ratchet_key,
+ kdf_info,
+ root_key, sender_chain[0].chain_key
+ );
+ }
+
+ MessageKey keys;
+ create_message_keys(sender_chain[0].chain_key, kdf_info, keys);
+ advance_chain_key(sender_chain[0].chain_key, sender_chain[0].chain_key);
+
+ std::size_t ciphertext_length = ratchet_cipher->ops->encrypt_ciphertext_length(
+ ratchet_cipher,
+ plaintext_length
+ );
+ std::uint32_t counter = keys.index;
+ _olm_curve25519_public_key const & ratchet_key =
+ sender_chain[0].ratchet_key.public_key;
+
+ olm::MessageWriter writer;
+
+ olm::encode_message(
+ writer, PROTOCOL_VERSION, counter, CURVE25519_KEY_LENGTH,
+ ciphertext_length,
+ output
+ );
+
+ olm::store_array(writer.ratchet_key, ratchet_key.public_key);
+
+ ratchet_cipher->ops->encrypt(
+ ratchet_cipher,
+ keys.key, sizeof(keys.key),
+ plaintext, plaintext_length,
+ writer.ciphertext, ciphertext_length,
+ output, output_length
+ );
+
+ olm::unset(keys);
+ return output_length;
+}
+
+
+std::size_t olm::Ratchet::decrypt_max_plaintext_length(
+ std::uint8_t const * input, std::size_t input_length
+) {
+ olm::MessageReader reader;
+ olm::decode_message(
+ reader, input, input_length,
+ ratchet_cipher->ops->mac_length(ratchet_cipher)
+ );
+
+ if (!reader.ciphertext) {
+ last_error = OlmErrorCode::OLM_BAD_MESSAGE_FORMAT;
+ return std::size_t(-1);
+ }
+
+ return ratchet_cipher->ops->decrypt_max_plaintext_length(
+ ratchet_cipher, reader.ciphertext_length);
+}
+
+
+std::size_t olm::Ratchet::decrypt(
+ std::uint8_t const * input, std::size_t input_length,
+ std::uint8_t * plaintext, std::size_t max_plaintext_length
+) {
+ olm::MessageReader reader;
+ olm::decode_message(
+ reader, input, input_length,
+ ratchet_cipher->ops->mac_length(ratchet_cipher)
+ );
+
+ if (reader.version != PROTOCOL_VERSION) {
+ last_error = OlmErrorCode::OLM_BAD_MESSAGE_VERSION;
+ return std::size_t(-1);
+ }
+
+ if (!reader.has_counter || !reader.ratchet_key || !reader.ciphertext) {
+ last_error = OlmErrorCode::OLM_BAD_MESSAGE_FORMAT;
+ return std::size_t(-1);
+ }
+
+ std::size_t max_length = ratchet_cipher->ops->decrypt_max_plaintext_length(
+ ratchet_cipher,
+ reader.ciphertext_length
+ );
+
+ if (max_plaintext_length < max_length) {
+ last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
+ return std::size_t(-1);
+ }
+
+ if (reader.ratchet_key_length != CURVE25519_KEY_LENGTH) {
+ last_error = OlmErrorCode::OLM_BAD_MESSAGE_FORMAT;
+ return std::size_t(-1);
+ }
+
+ ReceiverChain * chain = nullptr;
+
+ for (olm::ReceiverChain & receiver_chain : receiver_chains) {
+ if (0 == std::memcmp(
+ receiver_chain.ratchet_key.public_key, reader.ratchet_key,
+ CURVE25519_KEY_LENGTH
+ )) {
+ chain = &receiver_chain;
+ break;
+ }
+ }
+
+ std::size_t result = std::size_t(-1);
+
+ if (!chain) {
+ result = verify_mac_and_decrypt_for_new_chain(
+ *this, reader, plaintext, max_plaintext_length
+ );
+ } else if (chain->chain_key.index > reader.counter) {
+ /* Chain already advanced beyond the key for this message
+ * Check if the message keys are in the skipped key list. */
+ for (olm::SkippedMessageKey & skipped : skipped_message_keys) {
+ if (reader.counter == skipped.message_key.index
+ && 0 == std::memcmp(
+ skipped.ratchet_key.public_key, reader.ratchet_key,
+ CURVE25519_KEY_LENGTH
+ )
+ ) {
+ /* Found the key for this message. Check the MAC. */
+
+ result = verify_mac_and_decrypt(
+ ratchet_cipher, skipped.message_key, reader,
+ plaintext, max_plaintext_length
+ );
+
+ if (result != std::size_t(-1)) {
+ /* Remove the key from the skipped keys now that we've
+ * decoded the message it corresponds to. */
+ olm::unset(skipped);
+ skipped_message_keys.erase(&skipped);
+ return result;
+ }
+ }
+ }
+ } else {
+ result = verify_mac_and_decrypt_for_existing_chain(
+ *this, chain->chain_key,
+ reader, plaintext, max_plaintext_length
+ );
+ }
+
+ if (result == std::size_t(-1)) {
+ last_error = OlmErrorCode::OLM_BAD_MESSAGE_MAC;
+ return std::size_t(-1);
+ }
+
+ if (!chain) {
+ /* They have started using a new ephemeral ratchet key.
+ * We need to derive a new set of chain keys.
+ * We can discard our previous ephemeral ratchet key.
+ * We will generate a new key when we send the next message. */
+
+ chain = receiver_chains.insert();
+ olm::load_array(chain->ratchet_key.public_key, reader.ratchet_key);
+
+ // TODO: we've already done this once, in
+ // verify_mac_and_decrypt_for_new_chain(). we could reuse the result.
+ create_chain_key(
+ root_key, sender_chain[0].ratchet_key, chain->ratchet_key,
+ kdf_info, root_key, chain->chain_key
+ );
+
+ olm::unset(sender_chain[0]);
+ sender_chain.erase(sender_chain.begin());
+ }
+
+ while (chain->chain_key.index < reader.counter) {
+ olm::SkippedMessageKey & key = *skipped_message_keys.insert();
+ create_message_keys(chain->chain_key, kdf_info, key.message_key);
+ key.ratchet_key = chain->ratchet_key;
+ advance_chain_key(chain->chain_key, chain->chain_key);
+ }
+
+ advance_chain_key(chain->chain_key, chain->chain_key);
+
+ return result;
+}
--- /dev/null
+/* Copyright 2018-2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "olm/sas.h"
+#include "olm/base64.h"
+#include "olm/crypto.h"
+#include "olm/error.h"
+#include "olm/memory.h"
+
+struct OlmSAS {
+ enum OlmErrorCode last_error;
+ struct _olm_curve25519_key_pair curve25519_key;
+ uint8_t secret[CURVE25519_SHARED_SECRET_LENGTH];
+ int their_key_set;
+};
+
+const char * olm_sas_last_error(
+ const OlmSAS * sas
+) {
+ return _olm_error_to_string(sas->last_error);
+}
+
+enum OlmErrorCode olm_sas_last_error_code(
+ const OlmSAS * sas
+) {
+ return sas->last_error;
+}
+
+size_t olm_sas_size(void) {
+ return sizeof(OlmSAS);
+}
+
+OlmSAS * olm_sas(
+ void * memory
+) {
+ _olm_unset(memory, sizeof(OlmSAS));
+ return (OlmSAS *) memory;
+}
+
+size_t olm_clear_sas(
+ OlmSAS * sas
+) {
+ _olm_unset(sas, sizeof(OlmSAS));
+ return sizeof(OlmSAS);
+}
+
+size_t olm_create_sas_random_length(const OlmSAS * sas) {
+ return CURVE25519_KEY_LENGTH;
+}
+
+size_t olm_create_sas(
+ OlmSAS * sas,
+ void * random, size_t random_length
+) {
+ if (random_length < olm_create_sas_random_length(sas)) {
+ sas->last_error = OLM_NOT_ENOUGH_RANDOM;
+ return (size_t)-1;
+ }
+ _olm_crypto_curve25519_generate_key((uint8_t *) random, &sas->curve25519_key);
+ sas->their_key_set = 0;
+ return 0;
+}
+
+size_t olm_sas_pubkey_length(const OlmSAS * sas) {
+ return _olm_encode_base64_length(CURVE25519_KEY_LENGTH);
+}
+
+size_t olm_sas_get_pubkey(
+ OlmSAS * sas,
+ void * pubkey, size_t pubkey_length
+) {
+ if (pubkey_length < olm_sas_pubkey_length(sas)) {
+ sas->last_error = OLM_OUTPUT_BUFFER_TOO_SMALL;
+ return (size_t)-1;
+ }
+ _olm_encode_base64(
+ (const uint8_t *)sas->curve25519_key.public_key.public_key,
+ CURVE25519_KEY_LENGTH,
+ (uint8_t *)pubkey
+ );
+ return 0;
+}
+
+size_t olm_sas_set_their_key(
+ OlmSAS *sas,
+ void * their_key, size_t their_key_length
+) {
+ if (their_key_length < olm_sas_pubkey_length(sas)) {
+ sas->last_error = OLM_INPUT_BUFFER_TOO_SMALL;
+ return (size_t)-1;
+ }
+
+ size_t ret = _olm_decode_base64(their_key, their_key_length, their_key);
+ if (ret == (size_t)-1) {
+ sas->last_error = OLM_INVALID_BASE64;
+ return (size_t)-1;
+ }
+
+ _olm_crypto_curve25519_shared_secret(&sas->curve25519_key, their_key, sas->secret);
+ sas->their_key_set = 1;
+ return 0;
+}
+
+int olm_sas_is_their_key_set(
+ const OlmSAS *sas
+) {
+ return sas->their_key_set;
+}
+
+size_t olm_sas_generate_bytes(
+ OlmSAS * sas,
+ const void * info, size_t info_length,
+ void * output, size_t output_length
+) {
+ if (!sas->their_key_set) {
+ sas->last_error = OLM_SAS_THEIR_KEY_NOT_SET;
+ return (size_t)-1;
+ }
+ _olm_crypto_hkdf_sha256(
+ sas->secret, sizeof(sas->secret),
+ NULL, 0,
+ (const uint8_t *) info, info_length,
+ output, output_length
+ );
+ return 0;
+}
+
+size_t olm_sas_mac_length(
+ const OlmSAS *sas
+) {
+ return _olm_encode_base64_length(SHA256_OUTPUT_LENGTH);
+}
+
+// A version of the calculate mac function that produces base64 strings that are
+// compatible with other base64 implementations.
+size_t olm_sas_calculate_mac_fixed_base64(
+ OlmSAS * sas,
+ const void * input, size_t input_length,
+ const void * info, size_t info_length,
+ void * mac, size_t mac_length
+) {
+ if (mac_length < olm_sas_mac_length(sas)) {
+ sas->last_error = OLM_OUTPUT_BUFFER_TOO_SMALL;
+ return (size_t)-1;
+ }
+ if (!sas->their_key_set) {
+ sas->last_error = OLM_SAS_THEIR_KEY_NOT_SET;
+ return (size_t)-1;
+ }
+ uint8_t key[32];
+ _olm_crypto_hkdf_sha256(
+ sas->secret, sizeof(sas->secret),
+ NULL, 0,
+ (const uint8_t *) info, info_length,
+ key, 32
+ );
+
+ uint8_t temp_mac[32];
+ _olm_crypto_hmac_sha256(key, 32, input, input_length, temp_mac);
+ _olm_encode_base64((const uint8_t *)temp_mac, SHA256_OUTPUT_LENGTH, (uint8_t *)mac);
+
+ return 0;
+}
+
+
+size_t olm_sas_calculate_mac(
+ OlmSAS * sas,
+ const void * input, size_t input_length,
+ const void * info, size_t info_length,
+ void * mac, size_t mac_length
+) {
+ if (mac_length < olm_sas_mac_length(sas)) {
+ sas->last_error = OLM_OUTPUT_BUFFER_TOO_SMALL;
+ return (size_t)-1;
+ }
+ if (!sas->their_key_set) {
+ sas->last_error = OLM_SAS_THEIR_KEY_NOT_SET;
+ return (size_t)-1;
+ }
+ uint8_t key[32];
+ _olm_crypto_hkdf_sha256(
+ sas->secret, sizeof(sas->secret),
+ NULL, 0,
+ (const uint8_t *) info, info_length,
+ key, 32
+ );
+ _olm_crypto_hmac_sha256(key, 32, input, input_length, mac);
+ _olm_encode_base64((const uint8_t *)mac, SHA256_OUTPUT_LENGTH, (uint8_t *)mac);
+ return 0;
+}
+
+// for compatibility with an old version of Riot
+size_t olm_sas_calculate_mac_long_kdf(
+ OlmSAS * sas,
+ const void * input, size_t input_length,
+ const void * info, size_t info_length,
+ void * mac, size_t mac_length
+) {
+ if (mac_length < olm_sas_mac_length(sas)) {
+ sas->last_error = OLM_OUTPUT_BUFFER_TOO_SMALL;
+ return (size_t)-1;
+ }
+ if (!sas->their_key_set) {
+ sas->last_error = OLM_SAS_THEIR_KEY_NOT_SET;
+ return (size_t)-1;
+ }
+ uint8_t key[256];
+ _olm_crypto_hkdf_sha256(
+ sas->secret, sizeof(sas->secret),
+ NULL, 0,
+ (const uint8_t *) info, info_length,
+ key, 256
+ );
+ _olm_crypto_hmac_sha256(key, 256, input, input_length, mac);
+ _olm_encode_base64((const uint8_t *)mac, SHA256_OUTPUT_LENGTH, (uint8_t *)mac);
+ return 0;
+}
--- /dev/null
+/* Copyright 2015, 2016 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "olm/session.hh"
+#include "olm/cipher.h"
+#include "olm/crypto.h"
+#include "olm/account.hh"
+#include "olm/memory.hh"
+#include "olm/message.hh"
+#include "olm/pickle.hh"
+
+#include <cstring>
+#include <stdio.h>
+
+namespace {
+
+static const std::uint8_t PROTOCOL_VERSION = 0x3;
+
+static const std::uint8_t ROOT_KDF_INFO[] = "OLM_ROOT";
+static const std::uint8_t RATCHET_KDF_INFO[] = "OLM_RATCHET";
+static const std::uint8_t CIPHER_KDF_INFO[] = "OLM_KEYS";
+
+static const olm::KdfInfo OLM_KDF_INFO = {
+ ROOT_KDF_INFO, sizeof(ROOT_KDF_INFO) - 1,
+ RATCHET_KDF_INFO, sizeof(RATCHET_KDF_INFO) - 1
+};
+
+static const struct _olm_cipher_aes_sha_256 OLM_CIPHER =
+ OLM_CIPHER_INIT_AES_SHA_256(CIPHER_KDF_INFO);
+
+} // namespace
+
+olm::Session::Session(
+) : ratchet(OLM_KDF_INFO, OLM_CIPHER_BASE(&OLM_CIPHER)),
+ last_error(OlmErrorCode::OLM_SUCCESS),
+ received_message(false) {
+
+}
+
+
+std::size_t olm::Session::new_outbound_session_random_length() const {
+ return CURVE25519_RANDOM_LENGTH * 2;
+}
+
+
+std::size_t olm::Session::new_outbound_session(
+ olm::Account const & local_account,
+ _olm_curve25519_public_key const & identity_key,
+ _olm_curve25519_public_key const & one_time_key,
+ std::uint8_t const * random, std::size_t random_length
+) {
+ if (random_length < new_outbound_session_random_length()) {
+ last_error = OlmErrorCode::OLM_NOT_ENOUGH_RANDOM;
+ return std::size_t(-1);
+ }
+
+ _olm_curve25519_key_pair base_key;
+ _olm_crypto_curve25519_generate_key(random, &base_key);
+
+ _olm_curve25519_key_pair ratchet_key;
+ _olm_crypto_curve25519_generate_key(random + CURVE25519_RANDOM_LENGTH, &ratchet_key);
+
+ _olm_curve25519_key_pair const & alice_identity_key_pair = (
+ local_account.identity_keys.curve25519_key
+ );
+
+ received_message = false;
+ alice_identity_key = alice_identity_key_pair.public_key;
+ alice_base_key = base_key.public_key;
+ bob_one_time_key = one_time_key;
+
+ // Calculate the shared secret S via triple DH
+ std::uint8_t secret[3 * CURVE25519_SHARED_SECRET_LENGTH];
+ std::uint8_t * pos = secret;
+
+ _olm_crypto_curve25519_shared_secret(&alice_identity_key_pair, &one_time_key, pos);
+ pos += CURVE25519_SHARED_SECRET_LENGTH;
+ _olm_crypto_curve25519_shared_secret(&base_key, &identity_key, pos);
+ pos += CURVE25519_SHARED_SECRET_LENGTH;
+ _olm_crypto_curve25519_shared_secret(&base_key, &one_time_key, pos);
+
+ ratchet.initialise_as_alice(secret, sizeof(secret), ratchet_key);
+
+ olm::unset(base_key);
+ olm::unset(ratchet_key);
+ olm::unset(secret);
+
+ return std::size_t(0);
+}
+
+namespace {
+
+static bool check_message_fields(
+ olm::PreKeyMessageReader & reader, bool have_their_identity_key
+) {
+ bool ok = true;
+ ok = ok && (have_their_identity_key || reader.identity_key);
+ if (reader.identity_key) {
+ ok = ok && reader.identity_key_length == CURVE25519_KEY_LENGTH;
+ }
+ ok = ok && reader.message;
+ ok = ok && reader.base_key;
+ ok = ok && reader.base_key_length == CURVE25519_KEY_LENGTH;
+ ok = ok && reader.one_time_key;
+ ok = ok && reader.one_time_key_length == CURVE25519_KEY_LENGTH;
+ return ok;
+}
+
+} // namespace
+
+
+std::size_t olm::Session::new_inbound_session(
+ olm::Account & local_account,
+ _olm_curve25519_public_key const * their_identity_key,
+ std::uint8_t const * one_time_key_message, std::size_t message_length
+) {
+ olm::PreKeyMessageReader reader;
+ decode_one_time_key_message(reader, one_time_key_message, message_length);
+
+ if (!check_message_fields(reader, their_identity_key)) {
+ last_error = OlmErrorCode::OLM_BAD_MESSAGE_FORMAT;
+ return std::size_t(-1);
+ }
+
+ if (reader.identity_key && their_identity_key) {
+ bool same = 0 == std::memcmp(
+ their_identity_key->public_key, reader.identity_key, CURVE25519_KEY_LENGTH
+ );
+ if (!same) {
+ last_error = OlmErrorCode::OLM_BAD_MESSAGE_KEY_ID;
+ return std::size_t(-1);
+ }
+ }
+
+ olm::load_array(alice_identity_key.public_key, reader.identity_key);
+ olm::load_array(alice_base_key.public_key, reader.base_key);
+ olm::load_array(bob_one_time_key.public_key, reader.one_time_key);
+
+ olm::MessageReader message_reader;
+ decode_message(
+ message_reader, reader.message, reader.message_length,
+ ratchet.ratchet_cipher->ops->mac_length(ratchet.ratchet_cipher)
+ );
+
+ if (!message_reader.ratchet_key
+ || message_reader.ratchet_key_length != CURVE25519_KEY_LENGTH) {
+ last_error = OlmErrorCode::OLM_BAD_MESSAGE_FORMAT;
+ return std::size_t(-1);
+ }
+
+ _olm_curve25519_public_key ratchet_key;
+ olm::load_array(ratchet_key.public_key, message_reader.ratchet_key);
+
+ olm::OneTimeKey const * our_one_time_key = local_account.lookup_key(
+ bob_one_time_key
+ );
+
+ if (!our_one_time_key) {
+ last_error = OlmErrorCode::OLM_BAD_MESSAGE_KEY_ID;
+ return std::size_t(-1);
+ }
+
+ _olm_curve25519_key_pair const & bob_identity_key = (
+ local_account.identity_keys.curve25519_key
+ );
+ _olm_curve25519_key_pair const & bob_one_time_key = our_one_time_key->key;
+
+ // Calculate the shared secret S via triple DH
+ std::uint8_t secret[CURVE25519_SHARED_SECRET_LENGTH * 3];
+ std::uint8_t * pos = secret;
+ _olm_crypto_curve25519_shared_secret(&bob_one_time_key, &alice_identity_key, pos);
+ pos += CURVE25519_SHARED_SECRET_LENGTH;
+ _olm_crypto_curve25519_shared_secret(&bob_identity_key, &alice_base_key, pos);
+ pos += CURVE25519_SHARED_SECRET_LENGTH;
+ _olm_crypto_curve25519_shared_secret(&bob_one_time_key, &alice_base_key, pos);
+
+ ratchet.initialise_as_bob(secret, sizeof(secret), ratchet_key);
+
+ olm::unset(secret);
+
+ return std::size_t(0);
+}
+
+
+std::size_t olm::Session::session_id_length() const {
+ return SHA256_OUTPUT_LENGTH;
+}
+
+
+std::size_t olm::Session::session_id(
+ std::uint8_t * id, std::size_t id_length
+) {
+ if (id_length < session_id_length()) {
+ last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
+ return std::size_t(-1);
+ }
+ std::uint8_t tmp[CURVE25519_KEY_LENGTH * 3];
+ std::uint8_t * pos = tmp;
+ pos = olm::store_array(pos, alice_identity_key.public_key);
+ pos = olm::store_array(pos, alice_base_key.public_key);
+ pos = olm::store_array(pos, bob_one_time_key.public_key);
+ _olm_crypto_sha256(tmp, sizeof(tmp), id);
+ return session_id_length();
+}
+
+
+bool olm::Session::matches_inbound_session(
+ _olm_curve25519_public_key const * their_identity_key,
+ std::uint8_t const * one_time_key_message, std::size_t message_length
+) const {
+ olm::PreKeyMessageReader reader;
+ decode_one_time_key_message(reader, one_time_key_message, message_length);
+
+ if (!check_message_fields(reader, their_identity_key)) {
+ return false;
+ }
+
+ bool same = true;
+ if (reader.identity_key) {
+ same = same && 0 == std::memcmp(
+ reader.identity_key, alice_identity_key.public_key, CURVE25519_KEY_LENGTH
+ );
+ }
+ if (their_identity_key) {
+ same = same && 0 == std::memcmp(
+ their_identity_key->public_key, alice_identity_key.public_key,
+ CURVE25519_KEY_LENGTH
+ );
+ }
+ same = same && 0 == std::memcmp(
+ reader.base_key, alice_base_key.public_key, CURVE25519_KEY_LENGTH
+ );
+ same = same && 0 == std::memcmp(
+ reader.one_time_key, bob_one_time_key.public_key, CURVE25519_KEY_LENGTH
+ );
+ return same;
+}
+
+
+olm::MessageType olm::Session::encrypt_message_type() const {
+ if (received_message) {
+ return olm::MessageType::MESSAGE;
+ } else {
+ return olm::MessageType::PRE_KEY;
+ }
+}
+
+
+std::size_t olm::Session::encrypt_message_length(
+ std::size_t plaintext_length
+) const {
+ std::size_t message_length = ratchet.encrypt_output_length(
+ plaintext_length
+ );
+
+ if (received_message) {
+ return message_length;
+ }
+
+ return encode_one_time_key_message_length(
+ CURVE25519_KEY_LENGTH,
+ CURVE25519_KEY_LENGTH,
+ CURVE25519_KEY_LENGTH,
+ message_length
+ );
+}
+
+
+std::size_t olm::Session::encrypt_random_length() const {
+ return ratchet.encrypt_random_length();
+}
+
+
+std::size_t olm::Session::encrypt(
+ std::uint8_t const * plaintext, std::size_t plaintext_length,
+ std::uint8_t const * random, std::size_t random_length,
+ std::uint8_t * message, std::size_t message_length
+) {
+ if (message_length < encrypt_message_length(plaintext_length)) {
+ last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
+ return std::size_t(-1);
+ }
+ std::uint8_t * message_body;
+ std::size_t message_body_length = ratchet.encrypt_output_length(
+ plaintext_length
+ );
+
+ if (received_message) {
+ message_body = message;
+ } else {
+ olm::PreKeyMessageWriter writer;
+ encode_one_time_key_message(
+ writer,
+ PROTOCOL_VERSION,
+ CURVE25519_KEY_LENGTH,
+ CURVE25519_KEY_LENGTH,
+ CURVE25519_KEY_LENGTH,
+ message_body_length,
+ message
+ );
+ olm::store_array(writer.one_time_key, bob_one_time_key.public_key);
+ olm::store_array(writer.identity_key, alice_identity_key.public_key);
+ olm::store_array(writer.base_key, alice_base_key.public_key);
+ message_body = writer.message;
+ }
+
+ std::size_t result = ratchet.encrypt(
+ plaintext, plaintext_length,
+ random, random_length,
+ message_body, message_body_length
+ );
+
+ if (result == std::size_t(-1)) {
+ last_error = ratchet.last_error;
+ ratchet.last_error = OlmErrorCode::OLM_SUCCESS;
+ return result;
+ }
+
+ return result;
+}
+
+
+std::size_t olm::Session::decrypt_max_plaintext_length(
+ MessageType message_type,
+ std::uint8_t const * message, std::size_t message_length
+) {
+ std::uint8_t const * message_body;
+ std::size_t message_body_length;
+ if (message_type == olm::MessageType::MESSAGE) {
+ message_body = message;
+ message_body_length = message_length;
+ } else {
+ olm::PreKeyMessageReader reader;
+ decode_one_time_key_message(reader, message, message_length);
+ if (!reader.message) {
+ last_error = OlmErrorCode::OLM_BAD_MESSAGE_FORMAT;
+ return std::size_t(-1);
+ }
+ message_body = reader.message;
+ message_body_length = reader.message_length;
+ }
+
+ std::size_t result = ratchet.decrypt_max_plaintext_length(
+ message_body, message_body_length
+ );
+
+ if (result == std::size_t(-1)) {
+ last_error = ratchet.last_error;
+ ratchet.last_error = OlmErrorCode::OLM_SUCCESS;
+ }
+ return result;
+}
+
+
+std::size_t olm::Session::decrypt(
+ olm::MessageType message_type,
+ std::uint8_t const * message, std::size_t message_length,
+ std::uint8_t * plaintext, std::size_t max_plaintext_length
+) {
+ std::uint8_t const * message_body;
+ std::size_t message_body_length;
+ if (message_type == olm::MessageType::MESSAGE) {
+ message_body = message;
+ message_body_length = message_length;
+ } else {
+ olm::PreKeyMessageReader reader;
+ decode_one_time_key_message(reader, message, message_length);
+ if (!reader.message) {
+ last_error = OlmErrorCode::OLM_BAD_MESSAGE_FORMAT;
+ return std::size_t(-1);
+ }
+ message_body = reader.message;
+ message_body_length = reader.message_length;
+ }
+
+ std::size_t result = ratchet.decrypt(
+ message_body, message_body_length, plaintext, max_plaintext_length
+ );
+
+ if (result == std::size_t(-1)) {
+ last_error = ratchet.last_error;
+ ratchet.last_error = OlmErrorCode::OLM_SUCCESS;
+ return result;
+ }
+
+ received_message = true;
+ return result;
+}
+
+// make the description end with "..." instead of stopping abruptly with no
+// warning
+void elide_description(char *end) {
+ end[-3] = '.';
+ end[-2] = '.';
+ end[-1] = '.';
+ end[0] = '\0';
+}
+
+void olm::Session::describe(char *describe_buffer, size_t buflen) {
+ // how much of the buffer is remaining (this is an int rather than a size_t
+ // because it will get compared to the return value from snprintf)
+ int remaining = buflen;
+ // do nothing if we have a zero-length buffer, or if buflen > INT_MAX,
+ // resulting in an overflow
+ if (remaining <= 0) return;
+
+ describe_buffer[0] = '\0';
+ // we need at least 23 characters to get any sort of meaningful
+ // information, so bail if we don't have that. (But more importantly, we
+ // need it to be at least 4 so that elide_description doesn't go out of
+ // bounds.)
+ if (remaining < 23) return;
+
+ int size;
+
+ // check that snprintf didn't return an error or reach the end of the buffer
+#define CHECK_SIZE_AND_ADVANCE \
+ if (size > remaining) { \
+ return elide_description(describe_buffer + remaining - 1); \
+ } else if (size > 0) { \
+ describe_buffer += size; \
+ remaining -= size; \
+ } else { \
+ return; \
+ }
+
+ size = snprintf(
+ describe_buffer, remaining,
+ "sender chain index: %ld ", ratchet.sender_chain[0].chain_key.index
+ );
+ CHECK_SIZE_AND_ADVANCE;
+
+ size = snprintf(describe_buffer, remaining, "receiver chain indices:");
+ CHECK_SIZE_AND_ADVANCE;
+
+ for (size_t i = 0; i < ratchet.receiver_chains.size(); ++i) {
+ size = snprintf(
+ describe_buffer, remaining,
+ " %ld", ratchet.receiver_chains[i].chain_key.index
+ );
+ CHECK_SIZE_AND_ADVANCE;
+ }
+
+ size = snprintf(describe_buffer, remaining, " skipped message keys:");
+ CHECK_SIZE_AND_ADVANCE;
+
+ for (size_t i = 0; i < ratchet.skipped_message_keys.size(); ++i) {
+ size = snprintf(
+ describe_buffer, remaining,
+ " %ld", ratchet.skipped_message_keys[i].message_key.index
+ );
+ CHECK_SIZE_AND_ADVANCE;
+ }
+#undef CHECK_SIZE_AND_ADVANCE
+}
+
+namespace {
+// the master branch writes pickle version 1; the logging_enabled branch writes
+// 0x80000001.
+static const std::uint32_t SESSION_PICKLE_VERSION = 1;
+}
+
+std::size_t olm::pickle_length(
+ Session const & value
+) {
+ std::size_t length = 0;
+ length += olm::pickle_length(SESSION_PICKLE_VERSION);
+ length += olm::pickle_length(value.received_message);
+ length += olm::pickle_length(value.alice_identity_key);
+ length += olm::pickle_length(value.alice_base_key);
+ length += olm::pickle_length(value.bob_one_time_key);
+ length += olm::pickle_length(value.ratchet);
+ return length;
+}
+
+
+std::uint8_t * olm::pickle(
+ std::uint8_t * pos,
+ Session const & value
+) {
+ pos = olm::pickle(pos, SESSION_PICKLE_VERSION);
+ pos = olm::pickle(pos, value.received_message);
+ pos = olm::pickle(pos, value.alice_identity_key);
+ pos = olm::pickle(pos, value.alice_base_key);
+ pos = olm::pickle(pos, value.bob_one_time_key);
+ pos = olm::pickle(pos, value.ratchet);
+ return pos;
+}
+
+
+std::uint8_t const * olm::unpickle(
+ std::uint8_t const * pos, std::uint8_t const * end,
+ Session & value
+) {
+ uint32_t pickle_version;
+ pos = olm::unpickle(pos, end, pickle_version); UNPICKLE_OK(pos);
+
+ bool includes_chain_index;
+ switch (pickle_version) {
+ case 1:
+ includes_chain_index = false;
+ break;
+
+ case 0x80000001UL:
+ includes_chain_index = true;
+ break;
+
+ default:
+ value.last_error = OlmErrorCode::OLM_UNKNOWN_PICKLE_VERSION;
+ return nullptr;
+ }
+
+ pos = olm::unpickle(pos, end, value.received_message); UNPICKLE_OK(pos);
+ pos = olm::unpickle(pos, end, value.alice_identity_key); UNPICKLE_OK(pos);
+ pos = olm::unpickle(pos, end, value.alice_base_key); UNPICKLE_OK(pos);
+ pos = olm::unpickle(pos, end, value.bob_one_time_key); UNPICKLE_OK(pos);
+ pos = olm::unpickle(pos, end, value.ratchet, includes_chain_index); UNPICKLE_OK(pos);
+
+ return pos;
+}
--- /dev/null
+/* Copyright 2015 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "olm/utility.hh"
+#include "olm/crypto.h"
+
+
+olm::Utility::Utility(
+) : last_error(OlmErrorCode::OLM_SUCCESS) {
+}
+
+
+size_t olm::Utility::sha256_length() const {
+ return SHA256_OUTPUT_LENGTH;
+}
+
+
+size_t olm::Utility::sha256(
+ std::uint8_t const * input, std::size_t input_length,
+ std::uint8_t * output, std::size_t output_length
+) {
+ if (output_length < sha256_length()) {
+ last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
+ return std::size_t(-1);
+ }
+ _olm_crypto_sha256(input, input_length, output);
+ return SHA256_OUTPUT_LENGTH;
+}
+
+
+size_t olm::Utility::ed25519_verify(
+ _olm_ed25519_public_key const & key,
+ std::uint8_t const * message, std::size_t message_length,
+ std::uint8_t const * signature, std::size_t signature_length
+) {
+ if (signature_length < ED25519_SIGNATURE_LENGTH) {
+ last_error = OlmErrorCode::OLM_BAD_MESSAGE_MAC;
+ return std::size_t(-1);
+ }
+ if (!_olm_crypto_ed25519_verify(&key, message, message_length, signature)) {
+ last_error = OlmErrorCode::OLM_BAD_MESSAGE_MAC;
+ return std::size_t(-1);
+ }
+ return std::size_t(0);
+}