ccan: upgrade to get ccan/runes.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2022-07-16 14:25:11 +09:30
parent 3e672b784d
commit f65d3bb1fc
12 changed files with 1755 additions and 2 deletions

View file

@ -92,6 +92,7 @@ FEATURES :=
CCAN_OBJS := \
ccan-asort.o \
ccan-base64.o \
ccan-bitmap.o \
ccan-bitops.o \
ccan-breakpoint.o \
@ -127,6 +128,8 @@ CCAN_OBJS := \
ccan-ptr_valid.o \
ccan-rbuf.o \
ccan-read_write_all.o \
ccan-rune-coding.o \
ccan-rune-rune.o \
ccan-str-base32.o \
ccan-str-hex.o \
ccan-str.o \
@ -195,6 +198,8 @@ CCAN_HEADERS := \
$(CCANDIR)/ccan/ptrint/ptrint.h \
$(CCANDIR)/ccan/rbuf/rbuf.h \
$(CCANDIR)/ccan/read_write_all/read_write_all.h \
$(CCANDIR)/ccan/rune/internal.h \
$(CCANDIR)/ccan/rune/rune.h \
$(CCANDIR)/ccan/short_types/short_types.h \
$(CCANDIR)/ccan/str/base32/base32.h \
$(CCANDIR)/ccan/str/hex/hex.h \
@ -840,6 +845,8 @@ endif
ccan-breakpoint.o: $(CCANDIR)/ccan/breakpoint/breakpoint.c
@$(call VERBOSE, "cc $<", $(CC) $(CFLAGS) -c -o $@ $<)
ccan-base64.o: $(CCANDIR)/ccan/base64/base64.c
@$(call VERBOSE, "cc $<", $(CC) $(CFLAGS) -c -o $@ $<)
ccan-tal.o: $(CCANDIR)/ccan/tal/tal.c
@$(call VERBOSE, "cc $<", $(CC) $(CFLAGS) -c -o $@ $<)
ccan-tal-str.o: $(CCANDIR)/ccan/tal/str/str.c
@ -940,3 +947,7 @@ ccan-json_out.o: $(CCANDIR)/ccan/json_out/json_out.c
@$(call VERBOSE, "cc $<", $(CC) $(CFLAGS) -c -o $@ $<)
ccan-closefrom.o: $(CCANDIR)/ccan/closefrom/closefrom.c
@$(call VERBOSE, "cc $<", $(CC) $(CFLAGS) -c -o $@ $<)
ccan-rune-rune.o: $(CCANDIR)/ccan/rune/rune.c
@$(call VERBOSE, "cc $<", $(CC) $(CFLAGS) -c -o $@ $<)
ccan-rune-coding.o: $(CCANDIR)/ccan/rune/coding.c
@$(call VERBOSE, "cc $<", $(CC) $(CFLAGS) -c -o $@ $<)

View file

@ -1,3 +1,3 @@
CCAN imported from http://ccodearchive.net.
CCAN version: init-2540-g8448fd28
CCAN version: init-2541-g52b86922

View file

@ -116,7 +116,7 @@ ssize_t base64_decode_quartet_using_maps(const base64_maps_t *maps,
* @note sets errno = EDOM if src contains invalid characters
* @note sets errno = EINVAL if src is an invalid base64 tail
*/
ssize_t base64_decode_tail_using_maps(const base64_maps_t *maps, char *dest,
ssize_t base64_decode_tail_using_maps(const base64_maps_t *maps, char dest[3],
const char *src, size_t srclen);

1
ccan/ccan/rune/LICENSE Symbolic link
View file

@ -0,0 +1 @@
../../licenses/BSD-MIT

130
ccan/ccan/rune/_info Normal file
View file

@ -0,0 +1,130 @@
#include "config.h"
#include <stdio.h>
#include <string.h>
/**
* rune - Simple cookies you can extend (a-la Python runes class).
*
* This code is a form of cookies, but they are user-extensible, and
* contain a simple language to define what the cookie allows.
*
* A "rune" contains the hash of a secret (so the server can
* validate), such that you can add, but not subtract, conditions.
* This is a simplified form of Macaroons, See
* https://research.google/pubs/pub41892/ "Macaroons: Cookies with
* Contextual Caveats for Decentralized Authorization in the Cloud".
* It has one good idea, some extended ideas nobody implements, and
* lots and lots of words.
*
* License: BSD-MIT
* Author: Rusty Russell <rusty@rustcorp.com.au>
* Example:
* // Given "generate secret 1" outputs kr7AW-eJ2Munhv5ftu4rHqAnhxUpPQM8aOyWOmqiytk9MQ==
* // Given "add kr7AW-eJ2Munhv5ftu4rHqAnhxUpPQM8aOyWOmqiytk9MQ== uid=rusty" outputs Xyt5S6FKUnA2ppGB62c6HTPGojt2S7k2n7Cf7Tjj6zM9MSZ1aWQ9cnVzdHk=
* // Given "test secret Xyt5S6FKUnA2ppGB62c6HTPGojt2S7k2n7Cf7Tjj6zM9MSZ1aWQ9cnVzdHk= rusty" outputs PASSED
* // Given "test secret Xyt5S6FKUnA2ppGB62c6HTPGojt2S7k2n7Cf7Tjj6zM9MSZ1aWQ9cnVzdHk= notrusty" outputs FAILED: uid is not equal to rusty
* // Given "add Xyt5S6FKUnA2ppGB62c6HTPGojt2S7k2n7Cf7Tjj6zM9MSZ1aWQ9cnVzdHk= t\<1655958616" outputs _YBFmeAedqlLigWHAmvyyGGHRrnI40BRQGh2hWdSZ9E9MSZ1aWQ9cnVzdHkmdDwxNjU1OTU4NjE2
* // Given "test secret _YBFmeAedqlLigWHAmvyyGGHRrnI40BRQGh2hWdSZ9E9MSZ1aWQ9cnVzdHkmdDwxNjU1OTU4NjE2 rusty" outputs FAILED: t is greater or equal to 1655958616
* #include <ccan/err/err.h>
* #include <ccan/rune/rune.h>
* #include <ccan/str/str.h>
* #include <stdio.h>
* #include <sys/time.h>
*
* // We support two values: current time (t), and user id (uid).
* static const char *check(const tal_t *ctx,
* const struct rune *rune,
* const struct rune_altern *alt,
* char *uid)
* {
* // t= means current time, in seconds, as integer
* if (streq(alt->fieldname, "t")) {
* struct timeval now;
* gettimeofday(&now, NULL);
* return rune_alt_single_int(ctx, alt, now.tv_sec);
* }
* if (streq(alt->fieldname, "uid")) {
* return rune_alt_single_str(ctx, alt, uid, strlen(uid));
* }
* // Otherwise, field is missing
* return rune_alt_single_missing(ctx, alt);
* }
*
* int main(int argc, char *argv[])
* {
* struct rune *master, *rune;
*
* if (argc < 3)
* goto usage;
*
* if (streq(argv[1], "generate")) {
* // Make master, derive a unique_id'd rune.
* if (argc != 3 && argc != 4)
* goto usage;
* master = rune_new(NULL, (u8 *)argv[2], strlen(argv[2]), NULL);
* rune = rune_derive_start(NULL, master, argv[3]);
* } else if (streq(argv[1], "add")) {
* // Add a restriction
* struct rune_restr *restr;
* if (argc != 4)
* goto usage;
* rune = rune_from_base64(NULL, argv[2]);
* if (!rune)
* errx(1, "Bad rune");
* restr = rune_restr_from_string(NULL, argv[3], strlen(argv[3]));
* if (!restr)
* errx(1, "Bad restriction string");
* rune_add_restr(rune, restr);
* } else if (streq(argv[1], "test")) {
* const char *err;
* if (argc != 5)
* goto usage;
* master = rune_new(NULL, (u8 *)argv[2], strlen(argv[2]), NULL);
* if (!master)
* errx(1, "Bad master rune");
* rune = rune_from_base64(NULL, argv[3]);
* if (!rune)
* errx(1, "Bad rune");
* err = rune_test(NULL, master, rune, check, argv[4]);
* if (err)
* printf("FAILED: %s\n", err);
* else
* printf("PASSED\n");
* return 0;
* } else
* goto usage;
*
* printf("%s\n", rune_to_base64(NULL, rune));
* return 0;
*
* usage:
* errx(1, "Usage: %s generate <secret> <uniqueid> OR\n"
* "%s add <rune> <restriction> OR\n"
* "%s test <secret> <rune> <uid>", argv[0], argv[0], argv[0]);
* }
*/
int main(int argc, char *argv[])
{
/* Expect exactly one argument */
if (argc != 2)
return 1;
if (strcmp(argv[1], "depends") == 0) {
printf("ccan/base64\n");
printf("ccan/crypto/sha256\n");
printf("ccan/endian\n");
printf("ccan/mem\n");
printf("ccan/short_types\n");
printf("ccan/str/hex\n");
printf("ccan/tal/str\n");
printf("ccan/tal\n");
printf("ccan/typesafe_cb\n");
return 0;
}
if (strcmp(argv[1], "testdepends") == 0) {
printf("ccan/tal/grab_file\n");
return 0;
}
return 1;
}

422
ccan/ccan/rune/coding.c Normal file
View file

@ -0,0 +1,422 @@
/* MIT (BSD) license - see LICENSE file for details */
/* Routines to encode / decode a rune */
#include <ccan/rune/rune.h>
#include <ccan/rune/internal.h>
#include <ccan/str/hex/hex.h>
#include <ccan/tal/str/str.h>
#include <ccan/base64/base64.h>
#include <ccan/endian/endian.h>
#include <errno.h>
/* From Python base64.urlsafe_b64encode:
*
* The alphabet uses '-' instead of '+' and '_' instead of '/'.
*/
static const base64_maps_t base64_maps_urlsafe = {
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
"\xff\xff\xff\xff\xff" /* 0 */
"\xff\xff\xff\xff\xff" /* 5 */
"\xff\xff\xff\xff\xff" /* 10 */
"\xff\xff\xff\xff\xff" /* 15 */
"\xff\xff\xff\xff\xff" /* 20 */
"\xff\xff\xff\xff\xff" /* 25 */
"\xff\xff\xff\xff\xff" /* 30 */
"\xff\xff\xff\xff\xff" /* 35 */
"\xff\xff\xff\xff\xff" /* 40 */
"\x3e\xff\xff\x34\x35" /* 45 */
"\x36\x37\x38\x39\x3a" /* 50 */
"\x3b\x3c\x3d\xff\xff" /* 55 */
"\xff\xff\xff\xff\xff" /* 60 */
"\x00\x01\x02\x03\x04" /* 65 A */
"\x05\x06\x07\x08\x09" /* 70 */
"\x0a\x0b\x0c\x0d\x0e" /* 75 */
"\x0f\x10\x11\x12\x13" /* 80 */
"\x14\x15\x16\x17\x18" /* 85 */
"\x19\xff\xff\xff\xff" /* 90 */
"\x3f\xff\x1a\x1b\x1c" /* 95 */
"\x1d\x1e\x1f\x20\x21" /* 100 */
"\x22\x23\x24\x25\x26" /* 105 */
"\x27\x28\x29\x2a\x2b" /* 110 */
"\x2c\x2d\x2e\x2f\x30" /* 115 */
"\x31\x32\x33\xff\xff" /* 120 */
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 125 */
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 135 */
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 145 */
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 155 */
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 165 */
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 175 */
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 185 */
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 195 */
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 205 */
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 215 */
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 225 */
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 235 */
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 245 */
};
/* For encoding as a string */
struct wbuf {
size_t off, len;
char *buf;
};
static void to_wbuf(const char *s, size_t len, void *vwbuf)
{
struct wbuf *wbuf = vwbuf;
while (wbuf->off + len > wbuf->len)
tal_resize(&wbuf->buf, wbuf->len *= 2);
memcpy(wbuf->buf + wbuf->off, s, len);
wbuf->off += len;
}
/* For adding to sha256 */
static void to_sha256(const char *s, size_t len, void *vshactx)
{
struct sha256_ctx *shactx = vshactx;
sha256_update(shactx, s, len);
}
static void rune_altern_encode(const struct rune_altern *altern,
void (*cb)(const char *s, size_t len,
void *arg),
void *arg)
{
char cond = altern->condition;
const char *p;
cb(altern->fieldname, strlen(altern->fieldname), arg);
cb(&cond, 1, arg);
p = altern->value;
for (;;) {
char esc[2] = { '\\' };
size_t len = strcspn(p, "\\|&");
cb(p, len, arg);
if (!p[len])
break;
esc[1] = p[len];
cb(esc, 2, arg);
p++;
}
}
static void rune_restr_encode(const struct rune_restr *restr,
void (*cb)(const char *s, size_t len,
void *arg),
void *arg)
{
for (size_t i = 0; i < tal_count(restr->alterns); i++) {
if (i != 0)
cb("|", 1, arg);
rune_altern_encode(restr->alterns[i], cb, arg);
}
}
void rune_sha256_add_restr(struct sha256_ctx *shactx,
struct rune_restr *restr)
{
rune_restr_encode(restr, to_sha256, shactx);
rune_sha256_endmarker(shactx);
}
const char *rune_is_derived(const struct rune *source, const struct rune *rune)
{
if (!runestr_eq(source->version, rune->version))
return "Version mismatch";
return rune_is_derived_anyversion(source, rune);
}
const char *rune_is_derived_anyversion(const struct rune *source,
const struct rune *rune)
{
struct sha256_ctx shactx;
size_t i;
if (tal_count(rune->restrs) < tal_count(source->restrs))
return "Fewer restrictions than master";
/* If we add the same restrictions to source rune, do we match? */
shactx = source->shactx;
for (i = 0; i < tal_count(rune->restrs); i++) {
/* First restrictions must be identical */
if (i < tal_count(source->restrs)) {
if (!rune_restr_eq(source->restrs[i], rune->restrs[i]))
return "Does not match master restrictions";
} else
rune_sha256_add_restr(&shactx, rune->restrs[i]);
}
if (memcmp(shactx.s, rune->shactx.s, sizeof(shactx.s)) != 0)
return "Not derived from master";
return NULL;
}
static bool peek_char(const char *data, size_t len, char *c)
{
if (len == 0)
return false;
*c = *data;
return true;
}
static void drop_char(const char **data, size_t *len)
{
(*data)++;
(*len)--;
}
static void pull_invalid(const char **data, size_t *len)
{
*data = NULL;
*len = 0;
}
static bool pull_char(const char **data, size_t *len, char *c)
{
if (!peek_char(*data, *len, c)) {
pull_invalid(data, len);
return false;
}
drop_char(data, len);
return true;
}
static bool is_valid_cond(enum rune_condition cond)
{
switch (cond) {
case RUNE_COND_IF_MISSING:
case RUNE_COND_EQUAL:
case RUNE_COND_NOT_EQUAL:
case RUNE_COND_BEGINS:
case RUNE_COND_ENDS:
case RUNE_COND_CONTAINS:
case RUNE_COND_INT_LESS:
case RUNE_COND_INT_GREATER:
case RUNE_COND_LEXO_BEFORE:
case RUNE_COND_LEXO_AFTER:
case RUNE_COND_COMMENT:
return true;
}
return false;
}
/* Sets *more on success: true if another altern follows */
static struct rune_altern *rune_altern_decode(const tal_t *ctx,
const char **data, size_t *len,
bool *more)
{
struct rune_altern *alt = tal(ctx, struct rune_altern);
const char *strstart = *data;
char *value;
size_t strlen = 0;
char c;
/* Swallow field up to conditional */
for (;;) {
if (!pull_char(data, len, &c))
return tal_free(alt);
if (cispunct(c))
break;
strlen++;
}
alt->fieldname = tal_strndup(alt, strstart, strlen);
if (!is_valid_cond(c)) {
pull_invalid(data, len);
return tal_free(alt);
}
alt->condition = c;
/* Assign worst case. */
value = tal_arr(alt, char, *len + 1);
strlen = 0;
*more = false;
while (*len && pull_char(data, len, &c)) {
if (c == '|') {
*more = true;
break;
}
if (c == '&')
break;
if (c == '\\' && !pull_char(data, len, &c))
return tal_free(alt);
value[strlen++] = c;
}
value[strlen] = '\0';
tal_resize(&value, strlen + 1);
alt->value = value;
return alt;
}
static struct rune_restr *rune_restr_decode(const tal_t *ctx,
const char **data, size_t *len)
{
struct rune_restr *restr = tal(ctx, struct rune_restr);
size_t num_alts = 0;
bool more;
/* Must have at least one! */
restr->alterns = tal_arr(restr, struct rune_altern *, 0);
do {
struct rune_altern *alt;
alt = rune_altern_decode(restr, data, len, &more);
if (!alt)
return tal_free(restr);
tal_resize(&restr->alterns, num_alts+1);
restr->alterns[num_alts++] = alt;
} while (more);
return restr;
}
static struct rune *from_string(const tal_t *ctx,
const char *str,
const u8 *hash32)
{
size_t len = strlen(str);
struct rune *rune = tal(ctx, struct rune);
/* Now count up how many bytes we should have hashed: secret uses
* first block. */
rune->shactx.bytes = 64;
rune->restrs = tal_arr(rune, struct rune_restr *, 0);
rune->unique_id = NULL;
rune->version = NULL;
while (len) {
struct rune_restr *restr;
restr = rune_restr_decode(rune, &str, &len);
if (!restr)
return tal_free(rune);
if (!rune_add_restr(rune, restr))
return tal_free(rune);
}
/* Now we replace with canned hash state */
memcpy(rune->shactx.s, hash32, 32);
for (size_t i = 0; i < 8; i++)
rune->shactx.s[i] = be32_to_cpu(rune->shactx.s[i]);
return rune;
}
struct rune_restr *rune_restr_from_string(const tal_t *ctx,
const char *str,
size_t len)
{
struct rune_restr *restr;
restr = rune_restr_decode(NULL, &str, &len);
/* Don't allow trailing chars */
if (restr && len != 0)
restr = tal_free(restr);
return tal_steal(ctx, restr);
}
static void to_string(struct wbuf *wbuf, const struct rune *rune, u8 *hash32)
{
/* Copy hash in big-endian */
for (size_t i = 0; i < 8; i++) {
be32 v = cpu_to_be32(rune->shactx.s[i]);
memcpy(hash32 + i*4, &v, sizeof(v));
}
for (size_t i = 0; i < tal_count(rune->restrs); i++) {
if (i != 0)
to_wbuf("&", 1, wbuf);
rune_restr_encode(rune->restrs[i], to_wbuf, wbuf);
}
to_wbuf("", 1, wbuf);
}
struct rune *rune_from_base64n(const tal_t *ctx, const char *str, size_t len)
{
size_t blen;
u8 *data;
struct rune *rune;
data = tal_arr(NULL, u8, base64_decoded_length(len) + 1);
blen = base64_decode_using_maps(&base64_maps_urlsafe,
(char *)data, tal_bytelen(data),
str, len);
if (blen == -1)
goto fail;
if (blen < 32)
goto fail;
data[blen] = '\0';
/* Sanity check that it's a valid string! */
if (strlen((char *)data + 32) != blen - 32)
goto fail;
rune = from_string(ctx, (const char *)data + 32, data);
tal_free(data);
return rune;
fail:
tal_free(data);
return NULL;
}
struct rune *rune_from_base64(const tal_t *ctx, const char *str)
{
return rune_from_base64n(ctx, str, strlen(str));
}
char *rune_to_base64(const tal_t *ctx, const struct rune *rune)
{
u8 hash32[32];
char *ret;
size_t ret_len;
struct wbuf wbuf;
/* We're going to prepend hash */
wbuf.off = sizeof(hash32);
wbuf.len = 64;
wbuf.buf = tal_arr(NULL, char, wbuf.len);
to_string(&wbuf, rune, hash32);
/* Prepend hash */
memcpy(wbuf.buf, hash32, sizeof(hash32));
ret = tal_arr(ctx, char, base64_encoded_length(wbuf.off) + 1);
ret_len = base64_encode_using_maps(&base64_maps_urlsafe,
ret, tal_bytelen(ret),
wbuf.buf, wbuf.off - 1);
ret[ret_len] = '\0';
tal_free(wbuf.buf);
return ret;
}
struct rune *rune_from_string(const tal_t *ctx, const char *str)
{
u8 hash[32];
if (!hex_decode(str, 64, hash, sizeof(hash)))
return NULL;
if (str[64] != ':')
return NULL;
return from_string(ctx, str + 65, hash);
}
char *rune_to_string(const tal_t *ctx, const struct rune *rune)
{
u8 hash32[32];
struct wbuf wbuf;
/* We're going to prepend hash (in hex), plus colon */
wbuf.off = sizeof(hash32) * 2 + 1;
wbuf.len = 128;
wbuf.buf = tal_arr(ctx, char, wbuf.len);
to_string(&wbuf, rune, hash32);
hex_encode(hash32, sizeof(hash32), wbuf.buf, sizeof(hash32) * 2 + 1);
wbuf.buf[sizeof(hash32) * 2] = ':';
return wbuf.buf;
}

View file

@ -0,0 +1,8 @@
#ifndef CCAN_RUNE_INTERNAL_H
#define CCAN_RUNE_INTERNAL_H
/* MIT (BSD) license - see LICENSE file for details */
void rune_sha256_endmarker(struct sha256_ctx *shactx);
void rune_sha256_add_restr(struct sha256_ctx *shactx,
struct rune_restr *restr);
bool runestr_eq(const char *a, const char *b);
#endif /* CCAN_RUNE_INTERNAL_H */

491
ccan/ccan/rune/rune.c Normal file
View file

@ -0,0 +1,491 @@
/* MIT (BSD) license - see LICENSE file for details */
#include "config.h"
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <stdio.h>
#include <ccan/endian/endian.h>
#include <ccan/mem/mem.h>
#include <ccan/tal/str/str.h>
#include <ccan/rune/rune.h>
#include <ccan/rune/internal.h>
/* Helper to produce an id field */
static struct rune_restr *unique_id_restr(const tal_t *ctx,
const char *unique_id,
const char *version)
{
const char *id;
struct rune_restr *restr;
assert(!strchr(unique_id, '-'));
if (version)
id = tal_fmt(NULL, "%s-%s", unique_id, version);
else
id = tal_strdup(NULL, unique_id);
restr = rune_restr_new(ctx);
/* We use the empty field for this, since it's always present. */
rune_restr_add_altern(restr,
take(rune_altern_new(NULL, "", '=', take(id))));
return restr;
}
/* We pad between fields with something identical to the SHA end marker */
void rune_sha256_endmarker(struct sha256_ctx *shactx)
{
static const unsigned char pad[64] = {0x80};
be64 sizedesc;
sizedesc = cpu_to_be64((uint64_t)shactx->bytes << 3);
/* Add '1' bit to terminate, then all 0 bits, up to next block - 8. */
sha256_update(shactx, pad, 1 + ((128 - 8 - (shactx->bytes % 64) - 1) % 64));
/* Add number of bits of data (big endian) */
sha256_update(shactx, &sizedesc, 8);
}
struct rune *rune_new(const tal_t *ctx, const u8 *secret, size_t secret_len,
const char *version)
{
struct rune *rune = tal(ctx, struct rune);
assert(secret_len + 1 + 8 <= 64);
if (version)
rune->version = tal_strdup(rune, version);
else
rune->version = NULL;
rune->unique_id = NULL;
sha256_init(&rune->shactx);
sha256_update(&rune->shactx, secret, secret_len);
rune_sha256_endmarker(&rune->shactx);
rune->restrs = tal_arr(rune, struct rune_restr *, 0);
return rune;
}
struct rune *rune_dup(const tal_t *ctx, const struct rune *rune TAKES)
{
struct rune *dup;
if (taken(rune))
return tal_steal(ctx, (struct rune *)rune);
dup = tal_dup(ctx, struct rune, rune);
dup->restrs = tal_arr(dup, struct rune_restr *, tal_count(rune->restrs));
for (size_t i = 0; i < tal_count(rune->restrs); i++) {
dup->restrs[i] = rune_restr_dup(dup->restrs,
rune->restrs[i]);
}
return dup;
}
struct rune *rune_derive_start(const tal_t *ctx,
const struct rune *master,
const char *unique_id TAKES)
{
struct rune *rune = rune_dup(ctx, master);
/* If they provide a unique_id, it goes first. */
if (unique_id) {
if (taken(unique_id))
rune->unique_id = tal_steal(rune, unique_id);
else
rune->unique_id = tal_strdup(rune, unique_id);
rune_add_restr(rune, take(unique_id_restr(NULL,
rune->unique_id,
rune->version)));
} else {
assert(!rune->version);
}
return rune;
}
struct rune_altern *rune_altern_new(const tal_t *ctx,
const char *fieldname TAKES,
enum rune_condition condition,
const char *value TAKES)
{
struct rune_altern *altern = tal(ctx, struct rune_altern);
altern->condition = condition;
altern->fieldname = tal_strdup(altern, fieldname);
altern->value = tal_strdup(altern, value);
return altern;
}
struct rune_altern *rune_altern_dup(const tal_t *ctx,
const struct rune_altern *altern TAKES)
{
struct rune_altern *dup;
if (taken(altern))
return tal_steal(ctx, (struct rune_altern *)altern);
dup = tal(ctx, struct rune_altern);
dup->condition = altern->condition;
dup->fieldname = tal_strdup(dup, altern->fieldname);
dup->value = tal_strdup(dup, altern->value);
return dup;
}
struct rune_restr *rune_restr_dup(const tal_t *ctx,
const struct rune_restr *restr TAKES)
{
struct rune_restr *dup;
size_t num_altern;
if (taken(restr))
return tal_steal(ctx, (struct rune_restr *)restr);
num_altern = tal_count(restr->alterns);
dup = tal(ctx, struct rune_restr);
dup->alterns = tal_arr(dup, struct rune_altern *, num_altern);
for (size_t i = 0; i < num_altern; i++) {
dup->alterns[i] = rune_altern_dup(dup->alterns,
restr->alterns[i]);
}
return dup;
}
struct rune_restr *rune_restr_new(const tal_t *ctx)
{
struct rune_restr *restr = tal(ctx, struct rune_restr);
restr->alterns = tal_arr(restr, struct rune_altern *, 0);
return restr;
}
void rune_restr_add_altern(struct rune_restr *restr,
const struct rune_altern *alt TAKES)
{
size_t num = tal_count(restr->alterns);
tal_resize(&restr->alterns, num+1);
restr->alterns[num] = rune_altern_dup(restr->alterns, alt);
}
static bool is_unique_id(const struct rune_altern *alt)
{
return streq(alt->fieldname, "");
}
/* Return unique_id if valid, and sets *version */
static const char *extract_unique_id(const tal_t *ctx,
const struct rune_altern *alt,
const char **version)
{
size_t len;
/* Condition must be '='! */
if (alt->condition != '=')
return NULL;
len = strcspn(alt->value, "-");
if (alt->value[len])
*version = tal_strdup(ctx, alt->value + len + 1);
else
*version = NULL;
return tal_strndup(ctx, alt->value, len);
}
bool rune_add_restr(struct rune *rune,
const struct rune_restr *restr TAKES)
{
size_t num = tal_count(rune->restrs);
/* An empty fieldname is additional correctness checks */
for (size_t i = 0; i < tal_count(restr->alterns); i++) {
if (!is_unique_id(restr->alterns[i]))
continue;
/* Must be the only alternative */
if (tal_count(restr->alterns) != 1)
goto fail;
/* Must be the first restriction */
if (num != 0)
goto fail;
rune->unique_id = extract_unique_id(rune,
restr->alterns[i],
&rune->version);
if (!rune->unique_id)
goto fail;
}
tal_resize(&rune->restrs, num+1);
rune->restrs[num] = rune_restr_dup(rune->restrs, restr);
rune_sha256_add_restr(&rune->shactx, rune->restrs[num]);
return true;
fail:
if (taken(restr))
tal_free(restr);
return false;
}
static const char *rune_restr_test(const tal_t *ctx,
const struct rune *rune,
const struct rune_restr *restr,
const char *(*check)(const tal_t *ctx,
const struct rune *rune,
const struct rune_altern *alt,
void *arg),
void *arg)
{
size_t num = tal_count(restr->alterns);
const char **errs = tal_arr(NULL, const char *, num);
char *err;
/* Only one alternative has to pass! */
for (size_t i = 0; i < num; i++) {
errs[i] = check(errs, rune, restr->alterns[i], arg);
if (!errs[i]) {
tal_free(errs);
return NULL;
}
}
err = tal_fmt(ctx, "%s", errs[0]);
for (size_t i = 1; i < num; i++)
tal_append_fmt(&err, " AND %s", errs[i]);
tal_free(errs);
return err;
}
static const char *cond_test(const tal_t *ctx,
const struct rune_altern *alt,
const char *complaint,
bool cond)
{
if (cond)
return NULL;
return tal_fmt(ctx, "%s %s %s", alt->fieldname, complaint, alt->value);
}
static const char *integer_compare_valid(const tal_t *ctx,
const s64 *fieldval_int,
const struct rune_altern *alt,
s64 *runeval_int)
{
long l;
char *p;
if (!fieldval_int)
return tal_fmt(ctx, "%s is not an integer field",
alt->fieldname);
errno = 0;
l = strtol(alt->value, &p, 10);
if (p == alt->value
|| *p
|| ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE))
return tal_fmt(ctx, "%s is not a valid integer", alt->value);
*runeval_int = l;
return NULL;
}
static int lexo_order(const char *fieldval_str,
size_t fieldval_strlen,
const char *alt)
{
int ret = strncmp(fieldval_str, alt, fieldval_strlen);
/* If alt is same but longer, fieldval is < */
if (ret == 0 && strlen(alt) > fieldval_strlen)
ret = -1;
return ret;
}
static const char *rune_alt_single(const tal_t *ctx,
const struct rune_altern *alt,
const char *fieldval_str,
size_t fieldval_strlen,
const s64 *fieldval_int)
{
char strfield[STR_MAX_CHARS(s64) + 1];
s64 runeval_int = 0 /* gcc v9.4.0 gets upset with uninitiaized var at -O3 */;
const char *err;
/* Caller can't set both! */
if (fieldval_int) {
assert(!fieldval_str);
sprintf(strfield, "%"PRIi64, *fieldval_int);
fieldval_str = strfield;
fieldval_strlen = strlen(strfield);
}
switch (alt->condition) {
case RUNE_COND_IF_MISSING:
if (!fieldval_str)
return NULL;
return tal_fmt(ctx, "%s is present", alt->fieldname);
case RUNE_COND_EQUAL:
if (!fieldval_str)
return tal_fmt(ctx, "%s not present", alt->fieldname);
return cond_test(ctx, alt, "is not equal to",
memeqstr(fieldval_str, fieldval_strlen, alt->value));
case RUNE_COND_NOT_EQUAL:
if (!fieldval_str)
return tal_fmt(ctx, "%s not present", alt->fieldname);
return cond_test(ctx, alt, "is equal to",
!memeqstr(fieldval_str, fieldval_strlen, alt->value));
case RUNE_COND_BEGINS:
if (!fieldval_str)
return tal_fmt(ctx, "%s not present", alt->fieldname);
return cond_test(ctx, alt, "does not start with",
memstarts_str(fieldval_str, fieldval_strlen, alt->value));
case RUNE_COND_ENDS:
if (!fieldval_str)
return tal_fmt(ctx, "%s not present", alt->fieldname);
return cond_test(ctx, alt, "does not end with",
memends_str(fieldval_str, fieldval_strlen, alt->value));
case RUNE_COND_CONTAINS:
if (!fieldval_str)
return tal_fmt(ctx, "%s not present", alt->fieldname);
return cond_test(ctx, alt, "does not contain",
memmem(fieldval_str, fieldval_strlen,
alt->value, strlen(alt->value)));
case RUNE_COND_INT_LESS:
err = integer_compare_valid(ctx, fieldval_int,
alt, &runeval_int);
if (err)
return err;
return cond_test(ctx, alt, "is greater or equal to",
*fieldval_int < runeval_int);
case RUNE_COND_INT_GREATER:
err = integer_compare_valid(ctx, fieldval_int,
alt, &runeval_int);
if (err)
return err;
return cond_test(ctx, alt, "is less or equal to",
*fieldval_int > runeval_int);
case RUNE_COND_LEXO_BEFORE:
if (!fieldval_str)
return tal_fmt(ctx, "%s not present", alt->fieldname);
return cond_test(ctx, alt, "is equal to or ordered after",
lexo_order(fieldval_str, fieldval_strlen, alt->value) < 0);
case RUNE_COND_LEXO_AFTER:
if (!fieldval_str)
return tal_fmt(ctx, "%s not present", alt->fieldname);
return cond_test(ctx, alt, "is equal to or ordered before",
lexo_order(fieldval_str, fieldval_strlen, alt->value) > 0);
case RUNE_COND_COMMENT:
return NULL;
}
/* We should never create any other values! */
abort();
}
const char *rune_alt_single_str(const tal_t *ctx,
const struct rune_altern *alt,
const char *fieldval_str,
size_t fieldval_strlen)
{
return rune_alt_single(ctx, alt, fieldval_str, fieldval_strlen, NULL);
}
const char *rune_alt_single_int(const tal_t *ctx,
const struct rune_altern *alt,
s64 fieldval_int)
{
return rune_alt_single(ctx, alt, NULL, 0, &fieldval_int);
}
const char *rune_alt_single_missing(const tal_t *ctx,
const struct rune_altern *alt)
{
return rune_alt_single(ctx, alt, NULL, 0, NULL);
}
const char *rune_meets_criteria_(const tal_t *ctx,
const struct rune *rune,
const char *(*check)(const tal_t *ctx,
const struct rune *rune,
const struct rune_altern *alt,
void *arg),
void *arg)
{
for (size_t i = 0; i < tal_count(rune->restrs); i++) {
const char *err;
/* Don't "check" unique id */
if (i == 0 && is_unique_id(rune->restrs[i]->alterns[0]))
continue;
err = rune_restr_test(ctx, rune, rune->restrs[i], check, arg);
if (err)
return err;
}
return NULL;
}
const char *rune_test_(const tal_t *ctx,
const struct rune *master,
const struct rune *rune,
const char *(*check)(const tal_t *ctx,
const struct rune *rune,
const struct rune_altern *alt,
void *arg),
void *arg)
{
const char *err;
err = rune_is_derived(master, rune);
if (err)
return err;
return rune_meets_criteria_(ctx, rune, check, arg);
}
bool rune_altern_eq(const struct rune_altern *alt1,
const struct rune_altern *alt2)
{
return alt1->condition == alt2->condition
&& streq(alt1->fieldname, alt2->fieldname)
&& streq(alt1->value, alt2->value);
}
bool rune_restr_eq(const struct rune_restr *rest1,
const struct rune_restr *rest2)
{
if (tal_count(rest1->alterns) != tal_count(rest2->alterns))
return false;
for (size_t i = 0; i < tal_count(rest1->alterns); i++)
if (!rune_altern_eq(rest1->alterns[i], rest2->alterns[i]))
return false;
return true;
}
/* Equal, as in both NULL, or both non-NULL and matching */
bool runestr_eq(const char *a, const char *b)
{
if (a) {
if (!b)
return false;
return streq(a, b);
} else
return b == NULL;
}
bool rune_eq(const struct rune *rune1, const struct rune *rune2)
{
if (!runestr_eq(rune1->unique_id, rune2->unique_id))
return false;
if (!runestr_eq(rune1->version, rune2->version))
return false;
if (memcmp(rune1->shactx.s, rune2->shactx.s, sizeof(rune1->shactx.s)))
return false;
if (rune1->shactx.bytes != rune2->shactx.bytes)
return false;
if (memcmp(rune1->shactx.buf.u8, rune2->shactx.buf.u8,
rune1->shactx.bytes % 64))
return false;
if (tal_count(rune1->restrs) != tal_count(rune2->restrs))
return false;
for (size_t i = 0; i < tal_count(rune1->restrs); i++)
if (!rune_restr_eq(rune1->restrs[i], rune2->restrs[i]))
return false;
return true;
}

379
ccan/ccan/rune/rune.h Normal file
View file

@ -0,0 +1,379 @@
/* MIT (BSD) license - see LICENSE file for details */
#ifndef CCAN_RUNE_RUNE_H
#define CCAN_RUNE_RUNE_H
#include <ccan/crypto/sha256/sha256.h>
#include <ccan/typesafe_cb/typesafe_cb.h>
#include <ccan/tal/tal.h>
#include <ccan/short_types/short_types.h>
/* A rune is a series of restrictions. */
struct rune {
/* unique_id (if any) */
const char *unique_id;
/* Version (if any) */
const char *version;
/* SHA-2 256 of restrictions so far. */
struct sha256_ctx shactx;
/* Length given by tal_count() */
struct rune_restr **restrs;
};
/* A restriction is one or more alternatives (altern) */
struct rune_restr {
/* Length given by tal_count() */
struct rune_altern **alterns;
};
enum rune_condition {
RUNE_COND_IF_MISSING = '!',
RUNE_COND_EQUAL = '=',
RUNE_COND_NOT_EQUAL = '/',
RUNE_COND_BEGINS = '^',
RUNE_COND_ENDS = '$',
RUNE_COND_CONTAINS = '~',
RUNE_COND_INT_LESS = '<',
RUNE_COND_INT_GREATER = '>',
RUNE_COND_LEXO_BEFORE = '{',
RUNE_COND_LEXO_AFTER = '}',
RUNE_COND_COMMENT = '#',
};
/* An alternative is a utf-8 fieldname, a condition, and a value */
struct rune_altern {
enum rune_condition condition;
/* Strings. */
const char *fieldname, *value;
};
/**
* rune_new - Create an unrestricted rune from this secret.
* @ctx: tal context, or NULL. Freeing @ctx will free the returned rune.
* @secret: secret bytes.
* @secret_len: number of @secret bytes (must be 55 bytes or less)
* @version: if non-NULL, sets a version for this rune.
*
* This allocates a new, unrestricted rune (sometimes called a master rune).
*
* Setting a version allows for different interpretations of a rune if
* things change in future, at cost of some space when it's used.
*
* Example:
* u8 secret[16];
* struct rune *master;
*
* // A secret determined with a fair die roll!
* memset(secret, 5, sizeof(secret));
* master = rune_new(NULL, secret, sizeof(secret), NULL);
* assert(master);
*/
struct rune *rune_new(const tal_t *ctx, const u8 *secret, size_t secret_len,
const char *version);
/**
* rune_derive_start - Copy master rune, add a unique id.
* @ctx: context to allocate rune off
* @master: master rune.
* @unique_id: unique id; can be NULL, but that's not recommended.
*
* It's usually recommended to assign each rune a unique_id, so that
* specific runes can be blacklisted later (otherwise you need to disable
* all runes). This enlarges the rune string by '=<unique_id>' however.
*
* The rune version will be the same as the master: if that's non-zero,
* you *must* set unique_id.
*
* @unique_id cannot contain '-'.
*
* Example:
* struct rune *rune;
* // In reality, some global incrementing variable.
* const char *id = "1";
* rune = rune_derive_start(NULL, master, id);
* assert(rune);
*/
struct rune *rune_derive_start(const tal_t *ctx,
const struct rune *master,
const char *unique_id);
/**
* rune_dup - Copy a rune.
* @ctx: tal context, or NULL.
* @altern: the altern to copy.
*
* If @altern is take(), then simply returns it, otherwise copies.
*/
struct rune *rune_dup(const tal_t *ctx, const struct rune *rune TAKES);
/**
* rune_altern_new - Create a new alternative.
* @ctx: tal context, or NULL. Freeing @ctx will free the returned altern.
* @fieldname: the UTF-8 field for the altern. You can only have
* alphanumerics, '.', '-' and '_' here.
* @condition: the condition, defined above.
* @value: the value for comparison; use "" if you don't care. Any UTF-8 value
* is allowed.
*
* An altern is the basis of rune restrictions (technically, a restriction
* is one or more alterns, but it's often just one).
*
* Example:
* struct rune_altern *a1, *a2;
* a1 = rune_altern_new(NULL, "val", RUNE_COND_EQUAL, "7");
* a2 = rune_altern_new(NULL, "val2", '>', "-1");
* assert(a1 && a2);
*/
struct rune_altern *rune_altern_new(const tal_t *ctx,
const char *fieldname TAKES,
enum rune_condition condition,
const char *value TAKES);
/**
* rune_altern_dup - copy an alternative.
* @ctx: tal context, or NULL.
* @altern: the altern to copy.
*
* If @altern is take(), then simply returns it, otherwise copies.
*/
struct rune_altern *rune_altern_dup(const tal_t *ctx,
const struct rune_altern *altern TAKES);
/**
* rune_restr_new - Create a new (empty) restriction.
* @ctx: tal context, or NULL. Freeing @ctx will free the returned restriction.
*
* Example:
* struct rune_restr *restr = rune_restr_new(NULL);
* assert(restr);
*/
struct rune_restr *rune_restr_new(const tal_t *ctx);
/**
* rune_restr_dup - copy a restr.
* @ctx: tal context, or NULL.
* @restr: the restr to copy.
*
* If @resttr is take(), then simply returns it, otherwise copies.
*/
struct rune_restr *rune_restr_dup(const tal_t *ctx,
const struct rune_restr *restr TAKES);
/**
* rune_restr_add_altern - add an altern to this restriction
* @restr: the restriction to add to
* @alt: the altern.
*
* If the alt is take(alt) then the alt will be owned by the restriction,
* otherwise it's copied.
*
* Example:
* rune_restr_add_altern(restr, take(a1));
* rune_restr_add_altern(restr, take(a2));
*/
void rune_restr_add_altern(struct rune_restr *restr,
const struct rune_altern *alt TAKES);
/**
* rune_add_restr - add a restriction to this rune
* @rune: the rune to add to.
* @restr: the (non-empty) restriction.
*
* If the alt is take(alt) then the alt will be owned by the restr,
* otherwise it's copied (and all its children are copied!).
*
* This fails (and returns false) if restr tries to set unique_id/version
* and is not the first restriction, or has more than one alternative,
* or uses a non '=' condition.
*
* Example:
* rune_add_restr(rune, take(restr));
*/
bool rune_add_restr(struct rune *rune,
const struct rune_restr *restr TAKES);
/**
* rune_altern_eq - are two rune_altern equivalent?
* @alt1: the first
* @alt2: the second
*/
bool rune_altern_eq(const struct rune_altern *alt1,
const struct rune_altern *alt2);
/**
* rune_restr_eq - are two rune_restr equivalent?
* @rest1: the first
* @rest2: the second
*/
bool rune_restr_eq(const struct rune_restr *rest1,
const struct rune_restr *rest2);
/**
* rune_eq - are two runes equivalent?
* @rest1: the first
* @rest2: the second
*/
bool rune_eq(const struct rune *rune1, const struct rune *rune2);
/**
* rune_alt_single_str - helper to implement check().
* @ctx: context to allocate any error return from.
* @alt: alternative to test.
* @fieldval_str: field value as a string.
* @fieldval_strlen: length of @fieldval_str
*/
const char *rune_alt_single_str(const tal_t *ctx,
const struct rune_altern *alt,
const char *fieldval_str,
size_t fieldval_strlen);
/**
* rune_alt_single_int - helper to implement check().
* @ctx: context to allocate any error return from.
* @alt: alternative to test.
* @fieldval_int: field value as an integer.
*/
const char *rune_alt_single_int(const tal_t *ctx,
const struct rune_altern *alt,
s64 fieldval_int);
/**
* rune_alt_single_missing - helper to implement check().
* @ctx: context to allocate any error return from.
* @alt: alternative to test.
*
* Use this if alt->fieldname is unknown (it could still pass, if
* the test is that the fieldname is missing).
*/
const char *rune_alt_single_missing(const tal_t *ctx,
const struct rune_altern *alt);
/**
* rune_is_derived - is a rune derived from this other rune?
* @source: the base rune (usually the master rune)
* @rune: the rune to check.
*
* This is the first part of "is this rune valid?": does the cryptography
* check out, such that they validly made the rune from this source rune?
*
* It also checks that the versions match: if you want to allow more than
* one version, see rune_is_derived_anyversion.
*/
const char *rune_is_derived(const struct rune *source, const struct rune *rune);
/**
* rune_is_derived_anyversion - is a rune derived from this other rune?
* @source: the base rune (usually the master rune)
* @rune: the rune to check.
*
* This does not check source->version against rune->version: if you issue
* different rune versions you will need to check that yourself.
*/
const char *rune_is_derived_anyversion(const struct rune *source,
const struct rune *rune);
/**
* rune_meets_criteria - do we meet the criteria specified by the rune?
* @ctx: the tal context to allocate the returned error off.
* @rune: the rune to check.
* @check: the callback to check values
* @arg: data to hand to @check
*
* This is the second part of "is this rune valid?".
*/
const char *rune_meets_criteria_(const tal_t *ctx,
const struct rune *rune,
const char *(*check)(const tal_t *ctx,
const struct rune *rune,
const struct rune_altern *alt,
void *arg),
void *arg);
/* Typesafe wrapper */
#define rune_meets_criteria(ctx, rune, check, arg) \
rune_meets_criteria_(typesafe_cb_preargs(const char *, void *, \
(ctx), (rune), \
(check), (arg), \
const tal_t *, \
const struct rune *, \
const struct rune_altern *), \
(arg))
/**
* rune_test - is a rune authorized?
* @ctx: the tal context to allocate @errstr off.
* @master: the master rune created from secret.
* @rune: the rune to check.
* @errstr: if non-NULL, descriptive string of failure.
* @get: the callback to get values
* @arg: data to hand to callback
*
* Simple call for rune_is_derived() and rune_meets_criteria(). If
* it's not OK, returns non-NULL.
*/
const char *rune_test_(const tal_t *ctx,
const struct rune *master,
const struct rune *rune,
const char *(*check)(const tal_t *ctx,
const struct rune *rune,
const struct rune_altern *alt,
void *arg),
void *arg);
/* Typesafe wrapper */
#define rune_test(ctx_, master_, rune_, check_, arg_) \
rune_test_((ctx_), (master_), (rune_), \
typesafe_cb_preargs(const char *, void *, \
(check_), (arg_), \
const tal_t *, \
const struct rune *, \
const struct rune_altern *), \
(arg_))
/**
* rune_from_base64 - convert base64 string to rune.
* @ctx: context to allocate rune off.
* @str: base64 string.
*
* Returns NULL if it's malformed.
*/
struct rune *rune_from_base64(const tal_t *ctx, const char *str);
/**
* rune_from_base64n - convert base64 string to rune.
* @ctx: context to allocate rune off.
* @str: base64 string.
* @len: length of @str.
*
* Returns NULL if it's malformed.
*/
struct rune *rune_from_base64n(const tal_t *ctx, const char *str, size_t len);
/**
* rune_to_base64 - convert run to base64 string.
* @ctx: context to allocate rune off.
* @rune: the rune.
*
* Only returns NULL if you've allowed tal allocations to return NULL.
*/
char *rune_to_base64(const tal_t *ctx, const struct rune *rune);
/**
* This is a much more convenient working form.
*/
struct rune *rune_from_string(const tal_t *ctx, const char *str);
char *rune_to_string(const tal_t *ctx, const struct rune *rune);
/**
* rune_restr_from_string - convenience routine to parse a single restriction.
* @ctx: context to allocate rune off.
* @str: the string of form "<field><cond><val>[|<field><cond><val>]*"
* @len: the length of @str.
*
* This is useful for writing simple tests and making simple runes.
*/
struct rune_restr *rune_restr_from_string(const tal_t *ctx,
const char *str,
size_t len);
#endif /* CCAN_RUNE_RUNE_H */

View file

@ -0,0 +1,33 @@
#include <ccan/rune/rune.c>
#include <ccan/rune/coding.c>
#include <ccan/tal/str/str.h>
#include <ccan/tap/tap.h>
int main(void)
{
const char *str = "test string";
plan_tests(strlen(str) * strlen(str));
for (size_t i = 0; str[i]; i++) {
char *stra = strdup(str);
stra[i] = '\0';
for (size_t j = 0; str[j]; j++) {
char *strb = strdup(str);
strb[j] = '\0';
int lexo, strc;
lexo = lexo_order(str, i, strb);
strc = strcmp(stra, strb);
if (strc > 0)
ok1(lexo > 0);
else if (strc < 0)
ok1(lexo < 0);
else
ok1(lexo == 0);
free(strb);
}
free(stra);
}
/* This exits depending on whether all tests passed */
return exit_status();
}

127
ccan/ccan/rune/test/run.c Normal file
View file

@ -0,0 +1,127 @@
#include <ccan/rune/rune.c>
#include <ccan/rune/coding.c>
#include <ccan/tal/grab_file/grab_file.h>
#include <ccan/tal/str/str.h>
#include <ccan/tap/tap.h>
static const char *check(const tal_t *ctx,
const struct rune *rune,
const struct rune_altern *alt,
char **parts)
{
const char *val = NULL;
for (size_t i = 1; parts[i]; i++) {
if (strstarts(parts[i], alt->fieldname)
&& parts[i][strlen(alt->fieldname)] == '=')
val = parts[i] + strlen(alt->fieldname) + 1;
}
/* If it's an integer, hand it like that */
if (val) {
char *endp;
s64 v = strtol(val, &endp, 10);
if (*endp == '\0' && endp != val)
return rune_alt_single_int(ctx, alt, v);
return rune_alt_single_str(ctx, alt, val, strlen(val));
}
return rune_alt_single_missing(ctx, alt);
}
int main(void)
{
char *vecs;
char **lines;
static const u8 secret_zero[16];
struct rune *mr;
/* Test vector rune uses all-zero secret */
mr = rune_new(NULL, secret_zero, sizeof(secret_zero), NULL);
/* Python runes library generates test vectors */
vecs = grab_file(mr, "test/test_vectors.csv");
assert(vecs);
lines = tal_strsplit(mr, take(vecs), "\n", STR_NO_EMPTY);
plan_tests(343);
for (size_t i = 0; lines[i]; i++) {
struct rune *rune1, *rune2;
char **parts;
parts = tal_strsplit(lines, lines[i], ",", STR_EMPTY_OK);
if (streq(parts[0], "VALID")) {
diag("test %s %s", parts[0], parts[1]);
rune1 = rune_from_string(parts, parts[2]);
ok1(rune1);
rune2 = rune_from_base64(parts, parts[3]);
ok1(rune2);
ok1(rune_eq(rune1, rune2));
ok1(streq(rune_to_string(parts, rune2), parts[2]));
ok1(streq(rune_to_base64(parts, rune1), parts[3]));
ok1(rune_is_derived_anyversion(mr, rune1) == NULL);
ok1(rune_is_derived_anyversion(mr, rune2) == NULL);
if (parts[4]) {
if (parts[5])
ok1(streq(rune1->version, parts[5]));
ok1(streq(rune1->unique_id, parts[4]));
} else {
ok1(!rune1->version);
ok1(!rune1->unique_id);
}
mr->version = NULL;
} else if (streq(parts[0], "DERIVE")) {
struct rune_restr *restr;
diag("test %s %s", parts[0], parts[1]);
rune1 = rune_from_base64(parts, parts[2]);
ok1(rune1);
rune2 = rune_from_base64(parts, parts[3]);
ok1(rune2);
ok1(rune_is_derived_anyversion(mr, rune1) == NULL);
ok1(rune_is_derived_anyversion(mr, rune2) == NULL);
ok1(rune_is_derived_anyversion(rune1, rune2) == NULL);
restr = rune_restr_new(NULL);
for (size_t i = 4; parts[i]; i+=3) {
struct rune_altern *alt;
alt = rune_altern_new(NULL,
parts[i],
parts[i+1][0],
parts[i+2]);
rune_restr_add_altern(restr, take(alt));
}
rune_add_restr(rune1, take(restr));
ok1(rune_eq(rune1, rune2));
} else if (streq(parts[0], "MALFORMED")) {
diag("test %s %s", parts[0], parts[1]);
rune1 = rune_from_string(parts, parts[2]);
ok1(!rune1);
rune2 = rune_from_base64(parts, parts[3]);
ok1(!rune2);
} else if (streq(parts[0], "BAD DERIVATION")) {
diag("test %s %s", parts[0], parts[1]);
rune1 = rune_from_string(parts, parts[2]);
ok1(rune1);
rune2 = rune_from_base64(parts, parts[3]);
ok1(rune2);
ok1(rune_eq(rune1, rune2));
ok1(rune_is_derived(mr, rune1) != NULL);
ok1(rune_is_derived(mr, rune2) != NULL);
} else {
const char *err;
diag("test %s", parts[0]);
err = rune_test(parts, mr, rune1, check, parts);
if (streq(parts[0], "PASS")) {
ok1(!err);
} else {
assert(streq(parts[0], "FAIL"));
ok1(err);
}
}
}
tal_free(mr);
/* This exits depending on whether all tests passed */
return exit_status();
}

View file

@ -0,0 +1,151 @@
VALID,empty rune (secret = [0]*16),374708fff7719dd5979ec875d56cd2286f6d3cf7ec317a3b25632aab28ec37bb:,N0cI__dxndWXnsh11WzSKG9tPPfsMXo7JWMqqyjsN7s=
PASS
PASS,f1=1
PASS,f1=var
PASS,f1=\|\&\\
VALID,unique id 1,6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:=1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL09MQ==,1
VALID,unique id 2 version 1,4520773407c9658646326fdffe685ffbc3c8639a080dae4310b371830a205cf1:=2-1,RSB3NAfJZYZGMm_f_mhf-8PIY5oIDa5DELNxgwogXPE9Mi0x,2,1
VALID,f1 is missing,64a926b7185d7cf98e10a07dfc4e83d2a826896ebdb112ac964566fa2d50b464:f1!,ZKkmtxhdfPmOEKB9_E6D0qgmiW69sRKslkVm-i1QtGRmMSE=
PASS
PASS,f2=f1
FAIL,f1=1
FAIL,f1=var
VALID,f1 equals v1,745c6e39cd41ee9f8388af8ad882bae4ee4e8f6b373f7682cc64d8574551fa5f:f1=v1,dFxuOc1B7p-DiK-K2IK65O5Oj2s3P3aCzGTYV0VR-l9mMT12MQ==
PASS,f1=v1
FAIL,f1=v
FAIL,f1=v1a
FAIL
FAIL,f2=f1
VALID,f1 not equal v1,c9236a6532bfa8e24bec9a66e96af3fb355f817770e79c5a81f6dd0b5ed20e47:f1/v1,ySNqZTK_qOJL7Jpm6Wrz-zVfgXdw55xagfbdC17SDkdmMS92MQ==
PASS,f1=v2
PASS,f1=v
PASS,f1=v1a
FAIL
FAIL,f2=v1
VALID,f1 ends with v1,71f2a1ec9631efc75b01db15fe1f025327ab467f8a83e6bfa7506da222adc5a2:f1$v1,cfKh7JYx78dbAdsV_h8CUyerRn-Kg-a_p1BtoiKtxaJmMSR2MQ==
PASS,f1=v1
PASS,f1=2v1
FAIL,f1=v1a
FAIL
VALID,f1 starts with v1,5b13dffbbd9f7b191b0557595d10b22c0acec0c567f8efeba1d7d047927d7bce:f1^v1,WxPf-72fexkbBVdZXRCyLArOwMVn-O_rodfQR5J9e85mMV52MQ==
PASS,f1=v1
PASS,f1=v1a
FAIL,f1=2v1
FAIL
VALID,f1 contains v1,ccbe593b72e0ab29446e46796ccd0c775ecd7a327fcc9ddc00fd3910cdacca00:f1~v1,zL5ZO3LgqylEbkZ5bM0Md17NejJ_zJ3cAP05EM2sygBmMX52MQ==
PASS,f1=v1
PASS,f1=v1a
PASS,f1=2v1
PASS,f1=2v12
FAIL,f1=1v2
FAIL
VALID,f1 less than v1,caff52cedb9241dc00aea7cefc2b89b0a7445b1a4e34c48a5a2b91d2fe76d31f:f1<v1,yv9SztuSQdwArqfO_CuJsKdEWxpONMSKWiuR0v520x9mMTx2MQ==
FAIL,f1=1
FAIL,f1=2
FAIL,f1=v1
FAIL
VALID,f1 less than 1,f9776db54fb54c8dd6af20a65a0f210a752a0ee4d1b0a0e7fd9d7ef65af76f84:f1<1,-XdttU-1TI3WryCmWg8hCnUqDuTRsKDn_Z1-9lr3b4RmMTwx
PASS,f1=0
PASS,f1=-10000
FAIL,f1=1
FAIL,f1=10000
FAIL,f1=v1
FAIL
VALID,f1 greater than v1,2135748f1956d9dfa3c5b09ab6af9d6bb06a41c5bcf93d3f8105cb278af5ac56:f1>v1,ITV0jxlW2d-jxbCatq-da7BqQcW8-T0_gQXLJ4r1rFZmMT52MQ==
FAIL,f1=1
FAIL,f1=2
FAIL,f1=v1
FAIL
VALID,f1 greater than 1,84e9991dd941bac97cc681eefec5dd7ac3668a4490ca6b0f19f0e79d2bb9c746:f1>1,hOmZHdlBusl8xoHu_sXdesNmikSQymsPGfDnnSu5x0ZmMT4x
PASS,f1=2
PASS,f1=10000
FAIL,f1=1
FAIL,f1=-10000
FAIL,f1=0
FAIL,f1=v1
FAIL
VALID,f1 sorts before 11,b9653ad0dcad7e5ed183f98cdd7e616acd07a98cc66a107a67626290bf000236:f1{11,uWU60Nytfl7Rg_mM3X5has0HqYzGahB6Z2JikL8AAjZmMXsxMQ==
PASS,f1=0
PASS,f1=1
PASS,f1=
PASS,f1=/
FAIL,f1=11
FAIL,f1=111
FAIL,f1=v1
FAIL,f1=:
FAIL
VALID,f1 sorts after 11,8c1f6c7c39badc5dea850192a0a4c6e9dd96bf33d410adc5a08fc375b22a1a52:f1}11,jB9sfDm63F3qhQGSoKTG6d2WvzPUEK3FoI_DdbIqGlJmMX0xMQ==
PASS,f1=111
PASS,f1=v1
PASS,f1=:
FAIL,f1=0
FAIL,f1=1
FAIL,f1=
FAIL,f1=/
FAIL,f1=11
FAIL
VALID,f1 comment 11,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1#11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMSMxMQ==
PASS,f1=111
PASS,f1=v1
PASS,f1=:
PASS,f1=0
PASS,f1=1
PASS,f1=
PASS,f1=/
PASS,f1=11
PASS
VALID,f1=1 or f2=3,85c3643dc102f0a0d6f20eeb8c294092151688fae41ef7c8ec7272ab23918376:f1=1|f2=3,hcNkPcEC8KDW8g7rjClAkhUWiPrkHvfI7HJyqyORg3ZmMT0xfGYyPTM=
PASS,f1=1
PASS,f1=1,f2=2
PASS,f2=3
PASS,f1=var,f2=3
PASS,f1=1,f2=3
FAIL
FAIL,f1=2
FAIL,f1=f1
FAIL,f2=1
FAIL,f2=f1
DERIVE,unique_id 1 derivation,N0cI__dxndWXnsh11WzSKG9tPPfsMXo7JWMqqyjsN7s=,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL09MQ==,,=,1
DERIVE,unique_id 2 version 1 derivation,N0cI__dxndWXnsh11WzSKG9tPPfsMXo7JWMqqyjsN7s=,RSB3NAfJZYZGMm_f_mhf-8PIY5oIDa5DELNxgwogXPE9Mi0x,,=,2-1
DERIVE,f1=1 or f2=3,N0cI__dxndWXnsh11WzSKG9tPPfsMXo7JWMqqyjsN7s=,hcNkPcEC8KDW8g7rjClAkhUWiPrkHvfI7HJyqyORg3ZmMT0xfGYyPTM=,f1,=,1,f2,=,3
DERIVE,AND f3 contains &|\,hcNkPcEC8KDW8g7rjClAkhUWiPrkHvfI7HJyqyORg3ZmMT0xfGYyPTM=,S253BW1Lragb1CpCSLXYGt9AdrE4iFMlXmnO0alV5vlmMT0xfGYyPTMmZjN-XCZcfFxc,f3,~,&|\
PASS,f1=1,f3=&|\
PASS,f2=3,f3=&|\x
FAIL
FAIL,f1=1
FAIL,f2=3
FAIL,f1=1,f2=3
FAIL,f1=2,f3=&|\
FAIL,f2=2,f3=&|\
FAIL,f3=&|\
MALFORMED,unique id must use = not !,6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:!1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL0hMQ==
MALFORMED,unique id must use = not /,6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:/1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL0vMQ==
MALFORMED,unique id must use = not ^,6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:^1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL1eMQ==
MALFORMED,unique id must use = not $,6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:$1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL0kMQ==
MALFORMED,unique id must use = not ~,6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:~1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL1-MQ==
MALFORMED,unique id must use = not <,6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:<1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL08MQ==
MALFORMED,unique id must use = not >,6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:>1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL0-MQ==
MALFORMED,unique id must use = not },6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:}1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL19MQ==
MALFORMED,unique id must use = not {,6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:{1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL17MQ==
MALFORMED,unique id cannot be overridden,7a63a2966d38e6fed89256d4a6e983a6813bf084d4fc6c20b9cdaef24b23fa7e:=1-2&=3,emOilm045v7YklbUpumDpoE78ITU_Gwguc2u8ksj-n49MS0yJj0z
MALFORMED,version cannot be overridden,db823224f960976b3ee142ce8899fc7ea461b42617e7d16167b1886c5988c628:=1-2&=1-3,24IyJPlgl2s-4ULOiJn8fqRhtCYX59FhZ7GIbFmIxig9MS0yJj0xLTM=
MALFORMED,Bad condition ",76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1"11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMSIxMQ==
MALFORMED,Bad condition &,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1&11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMSYxMQ==
MALFORMED,Bad condition ',76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1'11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMScxMQ==
MALFORMED,Bad condition (,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1(11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMSgxMQ==
MALFORMED,Bad condition ),76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1)11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMSkxMQ==
MALFORMED,Bad condition *,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1*11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMSoxMQ==
MALFORMED,Bad condition +,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1+11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMSsxMQ==
MALFORMED,Bad condition -,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1-11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMS0xMQ==
MALFORMED,Bad condition .,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1.11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMS4xMQ==
MALFORMED,Bad condition :,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1:11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMToxMQ==
MALFORMED,Bad condition ;,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1;11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMTsxMQ==
MALFORMED,Bad condition ?,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1?11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMT8xMQ==
MALFORMED,Bad condition [,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1[11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMVsxMQ==
MALFORMED,Bad condition \,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1\11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMVwxMQ==
MALFORMED,Bad condition ],76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1]11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMV0xMQ==
MALFORMED,Bad condition _,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1_11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMV8xMQ==
MALFORMED,Bad condition `,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1`11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMWAxMQ==
MALFORMED,Bad condition |,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1|11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMXwxMQ==
BAD DERIVATION,Incremented sha,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0e:f1#11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw5mMSMxMQ==
BAD DERIVATION,Unchanged sha,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1#11&a=1,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMSMxMSZhPTE=
Can't render this file because it has a wrong number of fields in line 70.