]> git.sesse.net Git - vlc/commitdiff
update: RSA support
authorRafaël Carré <funman@videolan.org>
Thu, 11 Sep 2014 14:31:21 +0000 (16:31 +0200)
committerRafaël Carré <funman@videolan.org>
Thu, 11 Sep 2014 15:05:07 +0000 (17:05 +0200)
src/misc/update.h
src/misc/update_crypto.c

index 0d5b5a2fa38f27b104f50281bb6799e85995ff39..ddb89b6eeb4d04070fae12d2185a150bdb7e6b13 100644 (file)
@@ -52,12 +52,21 @@ struct public_key_packet_t
 
     uint8_t version;      /* we use only version 4 */
     uint8_t timestamp[4]; /* creation time of the key */
-    uint8_t algo;         /* we only use DSA */
+    uint8_t algo;         /* DSA or RSA */
+
     /* the multi precision integers, with their 2 bytes length header */
-    uint8_t p[2+3072/8];
-    uint8_t q[2+256/8];
-    uint8_t g[2+3072/8];
-    uint8_t y[2+3072/8];
+    union {
+        struct {
+            uint8_t p[2+3072/8];
+            uint8_t q[2+256/8];
+            uint8_t g[2+3072/8];
+            uint8_t y[2+3072/8];
+        } dsa ;
+        struct {
+            uint8_t n[2+4096/8];
+            uint8_t e[2+4096/8];
+        } rsa;
+    } sig;
 };
 
 /* used for public key and file signatures */
@@ -66,7 +75,7 @@ struct signature_packet_t
     uint8_t version; /* 3 or 4 */
 
     uint8_t type;
-    uint8_t public_key_algo;    /* DSA only */
+    uint8_t public_key_algo;    /* DSA or RSA */
     uint8_t digest_algo;
 
     uint8_t hash_verification[2];
@@ -90,12 +99,16 @@ struct signature_packet_t
 
 /* The part below is made of consecutive MPIs, their number and size being
  * public-key-algorithm dependent.
- *
- * Since we use DSA signatures only, there is 2 integers, r & s.
- * They range from 160 for 1k keys to 256 bits for 3k keys.
  */
-    uint8_t r[2+256/8];
-    uint8_t s[2+256/8];
+    union {
+        struct {
+            uint8_t r[2+256/8];
+            uint8_t s[2+256/8];
+        } dsa;
+        struct {
+            uint8_t s[2+4096/8];
+        } rsa;
+    } algo_specific;
 };
 
 typedef struct public_key_packet_t public_key_packet_t;
@@ -171,7 +184,7 @@ parse_public_key(
         const uint8_t *p_sig_issuer );
 
 /*
- * Verify an OpenPGP signature made on some hash, with some DSA public key
+ * Verify an OpenPGP signature made on some hash, with some public key
  */
 int
 verify_signature(signature_packet_t *sign, public_key_packet_t *p_key,
index e7b11fb646218134782a7b35af49bfbac8d2b48f..417b3ec821420497d4cdadd8f5d23ae6b382207b 100644 (file)
@@ -1,5 +1,5 @@
 /*****************************************************************************
- * update_crypto.c: DSA related functions used for updating
+ * update_crypto.c: OpenPGP related functions used for updating
  *****************************************************************************
  * Copyright © 2008-2009 VLC authors and VideoLAN
  * $Id$
@@ -104,7 +104,7 @@ static size_t read_mpi(uint8_t *dst, const uint8_t *buf, size_t buflen, size_t b
 
 /*
  * fill a public_key_packet_t structure from public key packet data
- * verify that it is a version 4 public key packet, using DSA
+ * verify that it is a version 4 public key packet, using DSA or RSA
  */
 static int parse_public_key_packet( public_key_packet_t *p_key,
                                     const uint8_t *p_buf, size_t i_packet_len )
@@ -122,14 +122,17 @@ static int parse_public_key_packet( public_key_packet_t *p_key,
     memcpy( p_key->timestamp, p_buf, 4 ); p_buf += 4; i_read += 4;
 
     p_key->algo      = *p_buf++; i_read++;
-    if( p_key->algo != GCRY_PK_DSA )
+    if( p_key->algo == GCRY_PK_DSA ) {
+        READ_MPI(p_key->sig.dsa.p, 3072);
+        READ_MPI(p_key->sig.dsa.q, 256);
+        READ_MPI(p_key->sig.dsa.g, 3072);
+        READ_MPI(p_key->sig.dsa.y, 3072);
+    } else if ( p_key->algo == GCRY_PK_RSA ) {
+        READ_MPI(p_key->sig.rsa.n, 4096);
+        READ_MPI(p_key->sig.rsa.e, 4096);
+    } else
         return VLC_EGENERIC;
 
-    READ_MPI(p_key->p, 3072);
-    READ_MPI(p_key->q, 256);
-    READ_MPI(p_key->g, 3072);
-    READ_MPI(p_key->y, 3072);
-
     if( i_read == i_packet_len )
         return VLC_SUCCESS;
 
@@ -175,7 +178,7 @@ static size_t parse_signature_v3_packet( signature_packet_t *p_sig,
 
 /*
  * fill a signature_packet_v4_t from signature packet data
- * verify that it was used with a DSA public key
+ * verify that it was used with a DSA or RSA public key
  */
 static size_t parse_signature_v4_packet( signature_packet_t *p_sig,
                                       const uint8_t *p_buf, size_t i_sig_len )
@@ -188,6 +191,8 @@ static size_t parse_signature_v4_packet( signature_packet_t *p_sig,
     p_sig->type = *p_buf++; i_read++;
 
     p_sig->public_key_algo = *p_buf++; i_read++;
+    if (p_sig->public_key_algo != GCRY_PK_DSA && p_sig->public_key_algo != GCRY_PK_RSA )
+            return 0;
 
     p_sig->digest_algo = *p_buf++; i_read++;
 
@@ -299,7 +304,7 @@ static int parse_signature_packet( signature_packet_t *p_sig,
     if( i_read == 0 ) /* signature packet parsing has failed */
         goto error;
 
-    if( p_sig->public_key_algo != GCRY_PK_DSA )
+    if( p_sig->public_key_algo != GCRY_PK_DSA && p_sig->public_key_algo != GCRY_PK_RSA )
         goto error;
 
     switch( p_sig->type )
@@ -318,8 +323,13 @@ static int parse_signature_packet( signature_packet_t *p_sig,
     p_buf--; /* rewind to the version byte */
     p_buf += i_read;
 
-    READ_MPI(p_sig->r, 256);
-    READ_MPI(p_sig->s, 256);
+    if( p_sig->public_key_algo == GCRY_PK_DSA ) {
+        READ_MPI(p_sig->algo_specific.dsa.r, 256);
+        READ_MPI(p_sig->algo_specific.dsa.s, 256);
+    } else if ( p_sig->public_key_algo == GCRY_PK_RSA ) {
+        READ_MPI(p_sig->algo_specific.rsa.s, 4096);
+    } else
+        goto error;
 
     assert( i_read == i_packet_len );
     if( i_read < i_packet_len ) /* some extra data, hm ? */
@@ -427,13 +437,95 @@ static int pgp_unarmor( const char *p_ibuf, size_t i_ibuf_len,
     return l_crc2 == l_crc ? p_opos - p_obuf : 0;
 }
 
+static int rsa_pkcs1_encode_sig(gcry_mpi_t *r_result, size_t size,
+                                const uint8_t *hash, int algo)
+{
+    uint8_t asn[100];
+    uint8_t frame[4096/8];
+
+    size_t asnlen = sizeof(asn);
+    size_t hashlen = gcry_md_get_algo_dlen(algo);
+
+    if (gcry_md_algo_info(algo, GCRYCTL_GET_ASNOID, asn, &asnlen))
+        return VLC_EGENERIC;
+
+    if (!hashlen || hashlen + asnlen + 4 > size)
+        return VLC_EGENERIC;
+
+    frame[0] = 0;
+    frame[1] = 1; /* block type */
+    int pad = size - hashlen - asnlen - 3 ;
+    memset (&frame[2], 0xff, pad );
+    frame[2+pad] = 0;
+    memcpy(&frame[3+pad], asn, asnlen);
+    memcpy(&frame[3+pad+asnlen], hash, hashlen);
+
+    if (gcry_mpi_scan(r_result, GCRYMPI_FMT_USG, frame, size, &size))
+        return VLC_EGENERIC;
+    return VLC_SUCCESS;
+}
+
+/*
+ * Verify an OpenPGP signature made with some RSA public key
+ */
+static int verify_signature_rsa( signature_packet_t *sign, public_key_packet_t *p_key,
+                      uint8_t *p_hash )
+{
+    int ret = VLC_EGENERIC;
+    /* the data to be verified (a hash) */
+    const char *hash_sexp_s = "(data(flags raw)(value %m))";
+    /* the public key */
+    const char *key_sexp_s = "(public-key(rsa(n %m)(e %m)))";
+    /* the signature */
+    const char *sig_sexp_s = "(sig-val(rsa(s%m)))";
+
+    size_t erroff;
+    gcry_mpi_t n, e, s, hash;
+    n = e = s = hash = NULL;
+    gcry_sexp_t key_sexp, hash_sexp, sig_sexp;
+    key_sexp = hash_sexp = sig_sexp = NULL;
+
+    int i_n_len = mpi_len( p_key->sig.rsa.n );
+    int i_e_len = mpi_len( p_key->sig.rsa.e );
+    if( gcry_mpi_scan( &n, GCRYMPI_FMT_USG, p_key->sig.rsa.n + 2, i_n_len, NULL ) ||
+        gcry_mpi_scan( &e, GCRYMPI_FMT_USG, p_key->sig.rsa.e + 2, i_e_len, NULL ) ||
+        gcry_sexp_build( &key_sexp, &erroff, key_sexp_s, n, e ) )
+        goto out;
+
+    uint8_t *p_s = sign->algo_specific.rsa.s;
+    int i_s_len = mpi_len( p_s );
+    if( gcry_mpi_scan( &s, GCRYMPI_FMT_USG, p_s + 2, i_s_len, NULL ) ||
+        gcry_sexp_build( &sig_sexp, &erroff, sig_sexp_s, s ) )
+        goto out;
+
+    if( rsa_pkcs1_encode_sig (&hash, i_n_len, p_hash, sign->digest_algo) ||
+        gcry_sexp_build( &hash_sexp, &erroff, hash_sexp_s, hash ) )
+        goto out;
+
+    if( gcry_pk_verify( sig_sexp, hash_sexp, key_sexp ) )
+        goto out;
+
+    ret = VLC_SUCCESS;
+
+out:
+    if( n ) gcry_mpi_release( n );
+    if( e ) gcry_mpi_release( e );
+    if( s ) gcry_mpi_release( s );
+    if( hash ) gcry_mpi_release( hash );
+    if( key_sexp ) gcry_sexp_release( key_sexp );
+    if( sig_sexp ) gcry_sexp_release( sig_sexp );
+    if( hash_sexp ) gcry_sexp_release( hash_sexp );
+    return ret;
+}
 
 /*
  * Verify an OpenPGP signature made with some DSA public key
  */
-int verify_signature( signature_packet_t *sign, public_key_packet_t *p_key,
+static int verify_signature_dsa( signature_packet_t *sign, public_key_packet_t *p_key,
                       uint8_t *p_hash )
 {
+    int ret = VLC_EGENERIC;
+
     /* the data to be verified (a hash) */
     const char *hash_sexp_s = "(data(flags raw)(value %m))";
     /* the public key */
@@ -447,41 +539,39 @@ int verify_signature( signature_packet_t *sign, public_key_packet_t *p_key,
     gcry_sexp_t key_sexp, hash_sexp, sig_sexp;
     key_sexp = hash_sexp = sig_sexp = NULL;
 
-    int i_p_len = mpi_len( p_key->p );
-    int i_q_len = mpi_len( p_key->q );
-    int i_g_len = mpi_len( p_key->g );
-    int i_y_len = mpi_len( p_key->y );
-    if( gcry_mpi_scan( &p, GCRYMPI_FMT_USG, p_key->p + 2, i_p_len, NULL ) ||
-        gcry_mpi_scan( &q, GCRYMPI_FMT_USG, p_key->q + 2, i_q_len, NULL ) ||
-        gcry_mpi_scan( &g, GCRYMPI_FMT_USG, p_key->g + 2, i_g_len, NULL ) ||
-        gcry_mpi_scan( &y, GCRYMPI_FMT_USG, p_key->y + 2, i_y_len, NULL ) ||
+    int i_p_len = mpi_len( p_key->sig.dsa.p );
+    int i_q_len = mpi_len( p_key->sig.dsa.q );
+    int i_g_len = mpi_len( p_key->sig.dsa.g );
+    int i_y_len = mpi_len( p_key->sig.dsa.y );
+    if( gcry_mpi_scan( &p, GCRYMPI_FMT_USG, p_key->sig.dsa.p + 2, i_p_len, NULL ) ||
+        gcry_mpi_scan( &q, GCRYMPI_FMT_USG, p_key->sig.dsa.q + 2, i_q_len, NULL ) ||
+        gcry_mpi_scan( &g, GCRYMPI_FMT_USG, p_key->sig.dsa.g + 2, i_g_len, NULL ) ||
+        gcry_mpi_scan( &y, GCRYMPI_FMT_USG, p_key->sig.dsa.y + 2, i_y_len, NULL ) ||
         gcry_sexp_build( &key_sexp, &erroff, key_sexp_s, p, q, g, y ) )
-        goto problem;
+        goto out;
 
-    uint8_t *p_r = sign->r;
-    uint8_t *p_s = sign->s;
+    uint8_t *p_r = sign->algo_specific.dsa.r;
+    uint8_t *p_s = sign->algo_specific.dsa.s;
     int i_r_len = mpi_len( p_r );
     int i_s_len = mpi_len( p_s );
     if( gcry_mpi_scan( &r, GCRYMPI_FMT_USG, p_r + 2, i_r_len, NULL ) ||
         gcry_mpi_scan( &s, GCRYMPI_FMT_USG, p_s + 2, i_s_len, NULL ) ||
         gcry_sexp_build( &sig_sexp, &erroff, sig_sexp_s, r, s ) )
-        goto problem;
+        goto out;
 
     int i_hash_len = gcry_md_get_algo_dlen (sign->digest_algo);
-    if (sign->public_key_algo == GCRY_PK_DSA) {
-        if (i_hash_len > i_q_len)
-            i_hash_len = i_q_len;
-    }
+    if (i_hash_len > i_q_len)
+        i_hash_len = i_q_len;
     if( gcry_mpi_scan( &hash, GCRYMPI_FMT_USG, p_hash, i_hash_len, NULL ) ||
         gcry_sexp_build( &hash_sexp, &erroff, hash_sexp_s, hash ) )
-        goto problem;
+        goto out;
 
     if( gcry_pk_verify( sig_sexp, hash_sexp, key_sexp ) )
-        goto problem;
+        goto out;
 
-    return VLC_SUCCESS;
+    ret = VLC_SUCCESS;
 
-problem:
+out:
     if( p ) gcry_mpi_release( p );
     if( q ) gcry_mpi_release( q );
     if( g ) gcry_mpi_release( g );
@@ -492,7 +582,22 @@ problem:
     if( key_sexp ) gcry_sexp_release( key_sexp );
     if( sig_sexp ) gcry_sexp_release( sig_sexp );
     if( hash_sexp ) gcry_sexp_release( hash_sexp );
-    return VLC_EGENERIC;
+
+    return ret;
+}
+
+/*
+ * Verify an OpenPGP signature made with some public key
+ */
+int verify_signature( signature_packet_t *sign, public_key_packet_t *p_key,
+                      uint8_t *p_hash )
+{
+    if (sign->public_key_algo == GCRY_PK_DSA)
+        return verify_signature_dsa(sign, p_key, p_hash);
+    else if (sign->public_key_algo == GCRY_PK_RSA)
+        return verify_signature_rsa(sign, p_key, p_hash);
+    else
+        return VLC_EGENERIC;
 }
 
 
@@ -744,6 +849,11 @@ uint8_t *hash_from_file( const char *psz_file, signature_packet_t *p_sig )
  */
 uint8_t *hash_from_public_key( public_key_t *p_pkey )
 {
+    const uint8_t pk_algo = p_pkey->key.algo;
+    size_t i_size;
+    size_t i_p_len, i_g_len, i_q_len, i_y_len;
+    size_t i_n_len, i_e_len;
+
     if( p_pkey->sig.version != 4 )
         return NULL;
 
@@ -754,19 +864,27 @@ uint8_t *hash_from_public_key( public_key_t *p_pkey )
     gcry_error_t error = 0;
     gcry_md_hd_t hd;
 
+    if (pk_algo == GCRY_PK_DSA) {
+        i_p_len = mpi_len( p_pkey->key.sig.dsa.p );
+        i_g_len = mpi_len( p_pkey->key.sig.dsa.g );
+        i_q_len = mpi_len( p_pkey->key.sig.dsa.q );
+        i_y_len = mpi_len( p_pkey->key.sig.dsa.y );
+
+        i_size = 6 + 2*4 + i_p_len + i_g_len + i_q_len + i_y_len;
+    } else if (pk_algo == GCRY_PK_RSA) {
+        i_n_len = mpi_len( p_pkey->key.sig.rsa.n );
+        i_e_len = mpi_len( p_pkey->key.sig.rsa.e );
+
+        i_size = 6 + 2*2 + i_n_len + i_e_len;
+    } else
+        return NULL;
+
     error = gcry_md_open( &hd, p_pkey->sig.digest_algo, 0 );
     if( error )
         return NULL;
 
     gcry_md_putc( hd, 0x99 );
 
-    size_t i_p_len = mpi_len( p_pkey->key.p );
-    size_t i_g_len = mpi_len( p_pkey->key.g );
-    size_t i_q_len = mpi_len( p_pkey->key.q );
-    size_t i_y_len = mpi_len( p_pkey->key.y );
-
-    size_t i_size = 6 + 2*4 + i_p_len + i_g_len + i_q_len + i_y_len;
-
     gcry_md_putc( hd, (i_size >> 8) & 0xff );
     gcry_md_putc( hd, i_size & 0xff );
 
@@ -774,17 +892,15 @@ uint8_t *hash_from_public_key( public_key_t *p_pkey )
     gcry_md_write( hd, p_pkey->key.timestamp, 4 );
     gcry_md_putc( hd, p_pkey->key.algo );
 
-    gcry_md_write( hd, (uint8_t*)&p_pkey->key.p, 2 );
-    gcry_md_write( hd, (uint8_t*)&p_pkey->key.p + 2, i_p_len );
-
-    gcry_md_write( hd, (uint8_t*)&p_pkey->key.q, 2 );
-    gcry_md_write( hd, (uint8_t*)&p_pkey->key.q + 2, i_q_len );
-
-    gcry_md_write( hd, (uint8_t*)&p_pkey->key.g, 2 );
-    gcry_md_write( hd, (uint8_t*)&p_pkey->key.g + 2, i_g_len );
-
-    gcry_md_write( hd, (uint8_t*)&p_pkey->key.y, 2 );
-    gcry_md_write( hd, (uint8_t*)&p_pkey->key.y + 2, i_y_len );
+    if (pk_algo == GCRY_PK_DSA) {
+        gcry_md_write( hd, (uint8_t*)&p_pkey->key.sig.dsa.p, 2 + i_p_len );
+        gcry_md_write( hd, (uint8_t*)&p_pkey->key.sig.dsa.q, 2 + i_q_len );
+        gcry_md_write( hd, (uint8_t*)&p_pkey->key.sig.dsa.g, 2 + i_g_len );
+        gcry_md_write( hd, (uint8_t*)&p_pkey->key.sig.dsa.y, 2 + i_y_len );
+    } else if (pk_algo == GCRY_PK_RSA) {
+        gcry_md_write( hd, (uint8_t*)&p_pkey->key.sig.rsa.n, 2 + i_n_len );
+        gcry_md_write( hd, (uint8_t*)&p_pkey->key.sig.rsa.e, 2 + i_e_len );
+    }
 
     gcry_md_putc( hd, 0xb4 );