]> git.ipfire.org Git - thirdparty/libsolv.git/commitdiff
Add support for ed25519 signature verification
authorMichael Schroeder <mls@suse.de>
Mon, 18 May 2020 16:17:42 +0000 (18:17 +0200)
committerMichael Schroeder <mls@suse.de>
Mon, 18 May 2020 16:17:42 +0000 (18:17 +0200)
I did this to learn about elliptic curve cryptography. All test
vectors from the ed25519 reference implementation verify.

ext/solv_ed25519.h [new file with mode: 0644]
ext/solv_pgpvrfy.c

diff --git a/ext/solv_ed25519.h b/ext/solv_ed25519.h
new file mode 100644 (file)
index 0000000..b48c341
--- /dev/null
@@ -0,0 +1,364 @@
+/*
+ * Copyright (c) 2020, SUSE LLC.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/* simple and slow ed25519 verification code. */
+
+#ifndef LIBSOLV_CHKSUM_H
+#include "chksum.h"
+#endif
+
+#define MPED25519_LEN (32 / MP_T_BYTES)
+
+#if MP_T_BYTES == 4
+# define MPED25519_CONST1(x) (mp_t)x
+#elif MP_T_BYTES == 1
+# define MPED25519_CONST1(x) (mp_t)(x >> 0), (mp_t)(x >> 8), (mp_t)(x >> 16), (mp_t)(x >> 24)
+#endif
+
+#define MPED25519_CONST(a, b, c, d, e, f, g, h) { \
+  MPED25519_CONST1(h), MPED25519_CONST1(g), MPED25519_CONST1(f), MPED25519_CONST1(e), \
+  MPED25519_CONST1(d), MPED25519_CONST1(c), MPED25519_CONST1(b), MPED25519_CONST1(a) \
+}
+
+static mp_t ed25519_q[] =              /* mod prime */
+  MPED25519_CONST(0x7FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFED);
+static mp_t ed25519_d[] =              /* -(121665/121666) */
+  MPED25519_CONST(0x52036CEE, 0x2B6FFE73, 0x8CC74079, 0x7779E898, 0x00700A4D, 0x4141D8AB, 0x75EB4DCA, 0x135978A3);
+static mp_t ed25519_p58[] =            /* (q-5)/8 */
+  MPED25519_CONST(0x0FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFD);
+static mp_t ed25519_sqrtm1[] =         /* sqrt(-1) */
+  MPED25519_CONST(0x2B832480, 0x4FC1DF0B, 0x2B4D0099, 0x3DFBD7A7, 0x2F431806, 0xAD2FE478, 0xC4EE1B27, 0x4A0EA0B0);
+static mp_t ed25519_n[] =              /* l */
+  MPED25519_CONST(0x10000000, 0x00000000, 0x00000000, 0x00000000, 0x14DEF9DE, 0xA2F79CD6, 0x5812631A, 0x5CF5D3ED);
+static mp_t ed25519_gx[] =             /* base point */
+  MPED25519_CONST(0x216936D3, 0xCD6E53FE, 0xC0A4E231, 0xFDD6DC5C, 0x692CC760, 0x9525A7B2, 0xC9562D60, 0x8F25D51A);
+static mp_t ed25519_gy[] =             /* base point */
+  MPED25519_CONST(0x66666666, 0x66666666, 0x66666666, 0x66666666, 0x66666666, 0x66666666, 0x66666666, 0x66666658);
+static mp_t ed25519_one[] =            /* 1 */
+  MPED25519_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001);
+static mp_t ed25519_qm2[] =            /* mod - 2 */
+  MPED25519_CONST(0x7FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFEB);
+
+/* small helpers to save some typing */
+static inline void
+mped25519_add(mp_t *target, mp_t *m1, mp_t *m2)
+{
+  mpadd(MPED25519_LEN, target, m1, m2, ed25519_q);
+}
+
+static inline void
+mped25519_sub(mp_t *target, mp_t *m1, mp_t *m2)
+{
+  mpsub(MPED25519_LEN, target, m1, m2, ed25519_q);
+}
+
+static void
+mped25519_mul(mp_t *target, mp_t *m1, mp_t *m2)
+{
+  mp_t tmp[MPED25519_LEN * 2];
+  int i, j;
+  mp2_t x;
+
+  mpzero(MPED25519_LEN * 2, tmp);
+  for (i = 0; i < MPED25519_LEN; i++)
+    {
+      for (x = 0, j = 0; j < MPED25519_LEN; j++)
+       {
+         x += (mp2_t)tmp[i + j] + (mp2_t)m1[i] * m2[j];
+         tmp[i + j] = x;
+         x >>= MP_T_BITS;
+       }
+      tmp[i + j] = x;
+    }
+  for (x = 0, i = 0; i < MPED25519_LEN; i++)
+    {
+      x += (mp2_t)tmp[i] + (mp2_t)tmp[i + MPED25519_LEN] * 38;
+      tmp[i] = x;
+      x >>= MP_T_BITS;
+    }
+  x *= 38;
+  if ((tmp[MPED25519_LEN - 1] & (1 << (MP_T_BITS - 1))) != 0)
+    {
+      x += 19;
+      tmp[MPED25519_LEN - 1] -= 1 << (MP_T_BITS - 1);
+    }
+  for (i = 0; x && i < MPED25519_LEN; i++)
+    {
+      x += (mp2_t)tmp[i];
+      tmp[i] = x;
+      x >>= MP_T_BITS;
+    }
+  if (tmp[MPED25519_LEN - 1] >= ed25519_q[MPED25519_LEN - 1])
+    mpsubmod(MPED25519_LEN, tmp, ed25519_q);
+  mpcpy(MPED25519_LEN, target, tmp);
+}
+
+static inline void
+mped25519_inv(mp_t *target, mp_t *a)
+{
+  mppow(MPED25519_LEN, target, a, MPED25519_LEN, ed25519_qm2, ed25519_q);
+}
+
+/* recover x coordinate from y and sign */
+static int
+mped25519_recover_x(mp_t *x, mp_t *y, int sign)
+{
+  mp_t num[MPED25519_LEN], den[MPED25519_LEN];
+  mp_t tmp1[MPED25519_LEN], tmp2[MPED25519_LEN];
+
+  if (!mpisless(MPED25519_LEN, y, ed25519_q))
+    return 0;
+  /* calculate num=y^2-1 and den=dy^2+1 */
+  mped25519_mul(num, y, y);
+  mped25519_mul(den, num, ed25519_d);
+  mped25519_sub(num, num, ed25519_one);
+  mped25519_add(den, den, ed25519_one);
+
+  /* calculate x = num*den^3 * (num*den^7)^p58 */
+  mped25519_mul(tmp1, den, den);       /* tmp1 = den^2 */
+  mped25519_mul(tmp2, tmp1, den);      /* tmp2 = den^3 */
+  mped25519_mul(tmp1, tmp2, tmp2);     /* tmp1 = den^6 */
+  mped25519_mul(x, tmp1, den);         /* x = den^7 */
+  mped25519_mul(tmp1, x, num);         /* tmp1 = num * den^7 */
+  mppow(MPED25519_LEN, x, tmp1, MPED25519_LEN, ed25519_p58, ed25519_q);        /* x = tmp1 ^ p58 */
+  mped25519_mul(tmp1, x, tmp2);                /* tmp1 = x * den^3 */
+  mped25519_mul(x, tmp1, num);         /* x = tmp1 * num */
+
+  /* check if den*x^2 == num */
+  mped25519_mul(tmp2, x, x);
+  mped25519_mul(tmp1, tmp2, den);
+  if (!mpisequal(MPED25519_LEN, tmp1, num)) {
+    mped25519_mul(tmp1, x, ed25519_sqrtm1);    /* x = x * sqrt(-1) */
+    mpcpy(MPED25519_LEN, x, tmp1);
+    mped25519_mul(tmp2, x, x);
+    mped25519_mul(tmp1, tmp2, den);
+    if (!mpisequal(MPED25519_LEN, tmp1, num))
+      return 0;
+  }
+  if (mpiszero(MPED25519_LEN, x))
+    return 0;                          /* (0,1) is bad */
+  if ((x[0] & 1) != sign)
+    mped25519_sub(x, ed25519_q, x);
+  return 1;
+}
+
+static void
+mped25519_setfromle(int len, mp_t *out, const unsigned char *buf, int bufl, int highmask)
+{
+  unsigned char lebuf[64];     /* bufl must be <= 64 */
+  int i;
+  for (i = 0; i < bufl; i++)
+    lebuf[bufl - 1 - i] = buf[i];
+  lebuf[0] &= highmask;
+  mpsetfrombe(len, out, lebuf, bufl);
+}
+
+static void
+mped25519_modn_fromle(mp_t *out, const unsigned char *buf, int bufl)
+{
+  mp_t tmp[64 / MP_T_BYTES];   /* bufl must be <= 64 */
+  mp_t tmp2[MPED25519_LEN];
+  int len = (bufl + MP_T_BYTES - 1) / MP_T_BYTES;
+  mped25519_setfromle(len, tmp, buf, bufl, 0xff);
+  mpzero(MPED25519_LEN, out);
+  mpmul_add(MPED25519_LEN, out, ed25519_one, len, tmp, tmp2, ed25519_n);
+}
+
+/* see https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html */
+/* M=7 add=6 */
+static void
+mped25519_ptdouble(mp_t *p_x, mp_t *p_y, mp_t *p_z)
+{
+  mp_t B[MPED25519_LEN], C[MPED25519_LEN], D[MPED25519_LEN];
+  mp_t F[MPED25519_LEN], H[MPED25519_LEN], J[MPED25519_LEN];
+  
+  mped25519_add(C, p_x, p_y);
+  mped25519_mul(B, C, C);
+  mped25519_mul(C, p_x, p_x);
+  mped25519_mul(D, p_y, p_y);
+  mped25519_sub(F, C, D);
+  mped25519_mul(H, p_z, p_z);
+  mped25519_add(H, H, H);
+  mped25519_add(J, F, H);
+  mped25519_add(H, C, D);
+  mped25519_mul(p_y, H, F);
+  mped25519_mul(p_z, J, F);
+  mped25519_sub(H, H, B);
+  mped25519_mul(p_x, J, H);
+}
+
+/* M=12 add=7 */
+static void
+mped25519_ptadd(mp_t *p_x, mp_t *p_y, mp_t *p_z, mp_t *q_x, mp_t *q_y, mp_t *q_z)
+{
+  mp_t A[MPED25519_LEN], B[MPED25519_LEN], C[MPED25519_LEN];
+  mp_t D[MPED25519_LEN], E[MPED25519_LEN], F[MPED25519_LEN];
+  mp_t G[MPED25519_LEN], H[MPED25519_LEN], J[MPED25519_LEN];
+  
+  mped25519_mul(A, p_z, q_z);
+  mped25519_mul(B, A, A);
+  mped25519_mul(C, p_x, q_x);
+  mped25519_mul(D, p_y, q_y);
+  mped25519_mul(F, ed25519_d, C);
+  mped25519_mul(E, F, D);
+  mped25519_sub(F, B, E);
+  mped25519_add(G, B, E);
+  mped25519_add(H, p_x, p_y);
+  mped25519_add(J, q_x, q_y);
+  mped25519_mul(p_x, H, J);
+  mped25519_sub(p_x, p_x, C);
+  mped25519_sub(p_x, p_x, D);
+  mped25519_mul(H, p_x, F);
+  mped25519_mul(p_x, H, A);
+  mped25519_add(H, D, C);
+  mped25519_mul(J, H, G);
+  mped25519_mul(p_y, J, A);
+  mped25519_mul(p_z, F, G);
+}
+
+static inline void
+mped25519_ptcpy(mp_t *p_x, mp_t *p_y, mp_t *p_z, mp_t *q_x, mp_t *q_y, mp_t *q_z)
+{
+  mpcpy(MPED25519_LEN, p_x, q_x);
+  mpcpy(MPED25519_LEN, p_y, q_y);
+  mpcpy(MPED25519_LEN, p_z, q_z);
+}
+
+#if 0
+static void
+mped25519_mpdump(mp_t *p, char *s, int c)
+{
+  if (c)
+    fprintf(stderr, "%c.", c);
+  mpdump(MPED25519_LEN, p, s);
+}
+
+static void
+mped25519_ptdump(mp_t *p_x, mp_t *p_y, mp_t *p_z, char *s)
+{
+  if (p_z)
+    {
+      mp_t zi[MPED25519_LEN], px[MPED25519_LEN], py[MPED25519_LEN];
+      mped25519_mpdump(p_x, s, 'X');
+      mped25519_mpdump(p_y, s, 'Y');
+      mped25519_mpdump(p_z, s, 'Z');
+      mped25519_inv(zi, p_z);
+      mped25519_mul(px, p_x, zi);
+      mped25519_mul(py, p_y, zi);
+      p_x = px;
+      p_y = py;
+    }
+  mped25519_mpdump(p_x, s, 'x');
+  mped25519_mpdump(p_y, s, 'y');
+}
+#endif
+
+
+/* scalar multiplication and add: r = s1*p1 + s2*p2 */
+/* needs about 13 + (256 - 32) / 2 = 125 point additions */
+static int
+mped25519_scmult2(mp_t *r_x, mp_t *r_y, mp_t *s1, mp_t *p1_x, mp_t * p1_y, mp_t *s2, mp_t *p2_x, mp_t * p2_y)
+{
+  mp_t p_x[MPED25519_LEN], p_y[MPED25519_LEN], p_z[MPED25519_LEN];
+  mp_t pi_z[MPED25519_LEN];
+  mp_t tabx[16][MPED25519_LEN];
+  mp_t taby[16][MPED25519_LEN];
+  mp_t tabz[16][MPED25519_LEN];
+  int i, x, dodouble = 0;
+
+  mpzero(MPED25519_LEN, p_x);
+  mpzero(MPED25519_LEN, p_y);
+  mpzero(MPED25519_LEN, p_z);
+  p_y[0] = p_z[0] = 1;
+  
+  /* setup table: tab[i * 4 + j] = i * p1 + j * p2 */
+  mped25519_ptcpy(tabx[0], taby[0], tabz[0], p_x, p_y, p_z);
+  for (i = 4; i < 16; i += 4)
+    mped25519_ptcpy(tabx[i], taby[i], tabz[i], p1_x, p1_y, ed25519_one);
+  mped25519_ptdouble(tabx[8], taby[8], tabz[8]);
+  mped25519_ptadd(tabx[12], taby[12], tabz[12], tabx[8], taby[8], tabz[8]);
+  mped25519_ptcpy(tabx[1], taby[1], tabz[1], p2_x, p2_y, ed25519_one);
+  for (i = 2; i < 16; i++)
+    {
+      if ((i & 3) == 0)
+       continue;
+      mped25519_ptcpy(tabx[i], taby[i], tabz[i], tabx[i - 1], taby[i - 1], tabz[i - 1]);
+      mped25519_ptadd(tabx[i], taby[i], tabz[i], p2_x, p2_y, ed25519_one);
+    }
+  /* now do the scalar multiplication */
+  for (i = 255; i >= 0; i--)
+    {
+      if (dodouble)
+        mped25519_ptdouble(p_x, p_y, p_z);
+      x  = s1[i / MP_T_BITS] & (1 << (i % MP_T_BITS)) ? 4 : 0;
+      x |= s2[i / MP_T_BITS] & (1 << (i % MP_T_BITS)) ? 1 : 0;
+      if (!x)
+       continue;
+      if (i > 0)
+       {
+         i--;
+         x <<= 1;
+         mped25519_ptdouble(p_x, p_y, p_z);
+         x |= s1[i / MP_T_BITS] & (1 << (i % MP_T_BITS)) ? 4 : 0;
+         x |= s2[i / MP_T_BITS] & (1 << (i % MP_T_BITS)) ? 1 : 0;
+       }
+      mped25519_ptadd(p_x, p_y, p_z, tabx[x], taby[x], tabz[x]);
+      dodouble = 1;
+    }
+#if 0
+  mped25519_ptdump(p_x, p_y, p_z, "r   = ");
+#endif
+  mped25519_inv(pi_z, p_z);
+  mped25519_mul(r_x, p_x, pi_z);
+  mped25519_mul(r_y, p_y, pi_z);
+  return mpiszero(MPED25519_LEN, p_z) ? 0 : 1;
+}
+
+static int
+mped25519(const unsigned char *pub, const unsigned char *sigr, const unsigned char *sigs, const unsigned char *data, unsigned int datal)
+{
+  unsigned char hbuf[64], rbuf[32];
+  int i;
+  mp_t pub_x[MPED25519_LEN], pub_y[MPED25519_LEN];
+  mp_t h[MPED25519_LEN], s[MPED25519_LEN];
+  mp_t r_x[MPED25519_LEN], r_y[MPED25519_LEN];
+  Chksum *chk;
+
+  if ((sigs[31] & 0xe0) != 0)
+    return 0;
+  /* uncompress pubkey, we invert the sign to get -pub */
+  mped25519_setfromle(MPED25519_LEN, pub_y, pub, 32, 0x7f);
+  if (!mped25519_recover_x(pub_x, pub_y, pub[31] & 0x80 ? 0 : 1))
+    return 0;          /* bad pubkey */
+#if 0
+  mped25519_ptdump(pub_x, pub_y, 0, "pub = ");
+#endif
+  /* calculate h and s */
+  chk = solv_chksum_create(REPOKEY_TYPE_SHA512);
+  if (!chk)
+    return 0;
+  solv_chksum_add(chk, sigr, 32);
+  solv_chksum_add(chk, pub, 32);
+  solv_chksum_add(chk, data, datal);
+  solv_chksum_free(chk, hbuf);
+  mped25519_modn_fromle(h, hbuf, 64);
+  mped25519_modn_fromle(s, sigs, 32);
+#if 0
+  mped25519_mpdump(s, "s     = ", 0);
+  mped25519_mpdump(h, "h     = ", 0);
+#endif
+  /* calculate r = s * G - h * pub */
+  if (!mped25519_scmult2(r_x, r_y, s, ed25519_gx, ed25519_gy, h, pub_x, pub_y))
+    return 0;
+  /* compress r into rbuf and verify that it matches sigr */
+  for (i = 0; i < 32; i++)
+    rbuf[i] = r_y[i / MP_T_BYTES] >> (8 * (i % MP_T_BYTES));
+  if ((r_x[0] & 1) != 0)
+    rbuf[31] |= 0x80;
+  return memcmp(sigr, rbuf, 32) == 0 ? 1 : 0;
+}
+
index 2af243757948d5162bec255880f55d78c9946c75..e4be692db460b10161a180cbc98ab70a4e1106f3 100644 (file)
@@ -1,11 +1,11 @@
 /*
- * Copyright (c) 2013, SUSE Inc.
+ * Copyright (c) 2013-2020, SUSE LLC.
  *
  * This program is licensed under the BSD license, read LICENSE.BSD
  * for further information
  */
 
-/* simple and slow rsa/dsa verification code. */
+/* simple and slow pgp signature verification code. */
 
 #include <stdio.h>
 #include <stdlib.h>
 #include "util.h"
 #include "solv_pgpvrfy.h"
 
+#ifndef ENABLE_PGPVRFY_ED25519
+#define ENABLE_PGPVRFY_ED25519 1
+#endif
+
 typedef unsigned int mp_t;
 typedef unsigned long long mp2_t;
 #define MP_T_BYTES 4
@@ -63,6 +67,30 @@ static void mpdump(int l, mp_t *a, char *s)
 }
 #endif
 
+/* subtract mod if target >= mod */
+static inline void mpsubmod(int len, mp_t *target, mp_t *mod)
+{
+  int i;
+  mp2_t n;
+  if (target[len - 1] < mod[len - 1])
+    return;
+  if (target[len - 1] == mod[len - 1])
+    {
+      for (i = len - 2; i >= 0; i--)
+       if (target[i] < mod[i])
+         return;
+       else if (target[i] > mod[i])
+         break;
+    }
+  /* target >= mod, subtract mod */
+  for (n = 0, i = 0; i < len; i++)
+    {
+      mp2_t n2 = (mp2_t)mod[i] + n;
+      n = n2 > target[i] ? 1 : 0;
+      target[i] -= (mp_t)n2;
+    }
+}
+
 /* target[len] = x, target = target % mod
  * assumes that target < (mod << MP_T_BITS)! */
 static void
@@ -100,25 +128,7 @@ mpdomod(int len, mp_t *target, mp2_t x, mp_t *mod)
     }
   target[i] = x;
   if (x >= mod[i])
-    {
-      mp_t n;
-      if (x == mod[i])
-       {
-         for (j = i - 1; j >= 0; j--)
-           if (target[j] < mod[j])
-             return;
-           else if (target[j] > mod[j])
-             break;
-       }
-      /* target >= mod, subtract mod */
-      n = 0;
-      for (j = 0; j <= i; j++)
-       {
-         mp2_t n2 = (mp2_t)mod[j] + n;
-         n = n2 > target[j] ? 1 : 0;
-         target[j] -= (mp_t)n2;
-       }
-    }
+    mpsubmod(i + 1, target, mod);
 }
 
 /* target += src * m */
@@ -164,7 +174,7 @@ mpmul_add(int len, mp_t *target, mp_t *m1, int m2len, mp_t *m2, mp_t *tmp, mp_t
   for (i = 0; i < j; i++)
     {
       if (m2[i])
-        mpmul_add_int(len, target, tmp, m2[i], mod);
+       mpmul_add_int(len, target, tmp, m2[i], mod);
       mpshift(len, tmp, mod);
     }
   if (m2[i])
@@ -276,6 +286,55 @@ mpdec(int len, mp_t *a)
       return;
 }
 
+#if ENABLE_PGPVRFY_ED25519
+/* target = m1 + m2 (m1, m2 < mod). target may be m1 or m2 */
+static void
+mpadd(int len, mp_t *target, mp_t *m1, mp_t *m2, mp_t *mod)
+{
+  int i;
+  mp2_t x = 0;
+  for (i = 0; i < len; i++)
+    {
+      x += (mp2_t)m1[i] + m2[i];
+      target[i] = x;
+      x >>= MP_T_BITS;
+    }
+  if (x || !mpisless(len, target, mod))
+    {
+      for (x = 0, i = 0; i < len; i++)
+       {
+         x = (mp2_t)target[i] - mod[i] - x;
+         target[i] = x;
+         x = x & ((mp2_t)1 << MP_T_BITS) ? 1 : 0;
+       }
+    }
+}
+
+/* target = m1 - m2 (m1, m2 < mod). target may be m1 or m2 */
+static void
+mpsub(int len, mp_t *target, mp_t *m1, mp_t *m2, mp_t *mod)
+{
+  int i;
+  mp2_t x = 0;
+  for (i = 0; i < len; i++)
+    {
+      x = (mp2_t)m1[i] - m2[i] - x;
+      target[i] = x;
+      x = x & ((mp2_t)1 << MP_T_BITS) ? 1 : 0;
+    }
+  if (x)
+    {
+      for (x = 0, i = 0; i < len; i++)
+       {
+         x += (mp2_t)target[i] + mod[i];
+         target[i] = x;
+         x >>= MP_T_BITS;
+       }
+    }
+}
+#endif
+
+
 static int
 mpdsa(int pl, mp_t *p, int ql, mp_t *q, mp_t *g, mp_t *y, mp_t *r, mp_t *s, int hl, mp_t *h)
 {
@@ -362,6 +421,10 @@ mprsa(int nl, mp_t *n, int el, mp_t *e, mp_t *m, mp_t *c)
   return 1;
 }
 
+#if ENABLE_PGPVRFY_ED25519
+# include "solv_ed25519.h"
+#endif
+
 /* create mp with size tbits from data with size dbits */
 static mp_t *
 mpbuild(const unsigned char *d, int dbits, int tbits, int *mplp)
@@ -397,6 +460,8 @@ findmpi(const unsigned char **mpip, int *mpilp, int maxbits, int *outlen)
   return mpi + 2;
 }
 
+/* sig: 0:algo 1:hash 2-:mpidata */
+/* pub: 0:algo 1-:mpidata */
 int
 solv_pgpvrfy(const unsigned char *pub, int publ, const unsigned char *sig, int sigl)
 {
@@ -522,6 +587,36 @@ solv_pgpvrfy(const unsigned char *pub, int publ, const unsigned char *sig, int s
        free(hx);
        break;
       }
+#if ENABLE_PGPVRFY_ED25519
+    case 22:           /* EdDSA */
+      {
+       unsigned char sigdata[64];
+       const unsigned char *r, *s;
+       int rlen, slen;
+
+       /* check the curve */
+       if (publ < 11 || memcmp(pub + 1, "\011\053\006\001\004\001\332\107\017\001", 10) != 0)
+         return 0;     /* we only support the Ed25519 curve */
+       /* the pubkey always has 7 + 256 bits */
+       if (publ != 1 + 10 + 2 + 1 + 32 || pub[1 + 10 + 0] != 1 || pub[1 + 10 + 1] != 7 || pub[1 + 10 + 2] != 0x40)
+         return 0;
+       mpi = sig + 2 + hashl;
+       mpil = sigl - (2 + hashl);
+       r = findmpi(&mpi, &mpil, 256, &rlen);
+       s = findmpi(&mpi, &mpil, 256, &slen);
+       if (!r || !s)
+         return 0;
+       memset(sigdata, 0, 64);
+       rlen = (rlen + 7) / 8;
+       slen = (slen + 7) / 8;
+       if (rlen)
+         memcpy(sigdata + 32 - rlen, r, rlen);
+       if (slen)
+         memcpy(sigdata + 64 - slen, s, rlen);
+       res = mped25519(pub + 1 + 10 + 2 + 1, sigdata, sigdata + 32, sig + 2, hashl);
+       break;
+      }
+#endif
     default:
       return 0;                /* unsupported pubkey algo */
     }