+/* --- @cmd_verify@ --- */
+
+static unsigned xdigit(char c)
+{
+ if ('A' <= c && c <= 'Z') return (c + 10 - 'A');
+ if ('a' <= c && c <= 'z') return (c + 10 - 'a');
+ if ('0' <= c && c <= '9') return (c - '0');
+ return (~0u);
+}
+
+static void unhexify(octet *q, char *p, size_t n)
+{
+ unsigned a = 0;
+ int i = 0;
+
+ for (;;) {
+ if (*p == '-' || *p == ':' || isspace((unsigned char)*p)) {
+ p++;
+ continue;
+ }
+ if (!n && !*p)
+ break;
+ if (!*p)
+ die(EXIT_FAILURE, "hex string too short");
+ if (!isxdigit((unsigned char)*p))
+ die(EXIT_FAILURE, "bad hex string");
+ if (!n)
+ die(EXIT_FAILURE, "hex string too long");
+ a = (a << 4) | xdigit(*p++);
+ i++;
+ if (i == 2) {
+ *q++ = U8(a);
+ a = 0;
+ i = 0;
+ n--;
+ }
+ }
+}
+
+static int cmd_verify(int argc, char *argv[])
+{
+ key_file f;
+ int rc = 0;
+ const gchash *ch = &rmd160;
+ ghash *h;
+ key *k;
+ octet *buf;
+ const octet *fpr;
+ key_filter kf = { KF_NONSECRET, KF_NONSECRET };
+
+ for (;;) {
+ static struct option opt[] = {
+ { "filter", OPTF_ARGREQ, 0, 'f' },
+ { "algorithm", OPTF_ARGREQ, 0, 'a' },
+ { 0, 0, 0, 0 }
+ };
+ int i = mdwopt(argc, argv, "+f:a:", opt, 0, 0, 0);
+ if (i < 0)
+ break;
+ switch (i) {
+ case 'f': {
+ char *p;
+ int err = key_readflags(optarg, &p, &kf.f, &kf.m);
+ if (err || *p)
+ die(EXIT_FAILURE, "bad filter string `%s'", optarg);
+ } break;
+ case 'a':
+ if ((ch = ghash_byname(optarg)) == 0)
+ die(EXIT_FAILURE, "unknown hash algorithm `%s'", optarg);
+ break;
+ default:
+ rc = 1;
+ break;
+ }
+ }
+
+ argv += optind; argc -= optind;
+ if (rc || argc != 2)
+ die(EXIT_FAILURE, "Usage: verify [-f FILTER] TAG FINGERPRINT");
+
+ doopen(&f, KOPEN_READ);
+
+ if ((k = key_bytag(&f, argv[0])) == 0)
+ die(EXIT_FAILURE, "key `%s' not found", argv[0]);
+ buf = xmalloc(ch->hashsz);
+ unhexify(buf, argv[1], ch->hashsz);
+ h = GH_INIT(ch);
+ if (!key_fingerprint(k, h, &kf))
+ die(EXIT_FAILURE, "key has no fingerprintable components (as filtered)");
+ fpr = GH_DONE(h, 0);
+ if (memcmp(fpr, buf, ch->hashsz) != 0)
+ die(EXIT_FAILURE, "key fingerprint mismatch");
+ doclose(&f);
+ return (0);
+}
+