Factorized and reworked fingerprint comparison code
authorEdouard Tisserant <edouard@pep-project.org>
Wed, 03 May 2017 18:11:49 +0200
changeset 1746498b0671ff4e
parent 1744 4d42c179c6bb
child 1747 061d8bf91506
Factorized and reworked fingerprint comparison code
src/message_api.c
src/pEp_internal.h
test/trustwords_test.cc
     1.1 --- a/src/message_api.c	Tue May 02 09:32:38 2017 +0200
     1.2 +++ b/src/message_api.c	Wed May 03 18:11:49 2017 +0200
     1.3 @@ -2244,88 +2244,6 @@
     1.4  	return PEP_color_no_color;
     1.5  }
     1.6  
     1.7 -static bool _is_valid_hex(const char* hexstr) {
     1.8 -    if (!hexstr)
     1.9 -        return false;
    1.10 -
    1.11 -    const char* curr = hexstr;
    1.12 -    char currchar;
    1.13 -
    1.14 -    for (currchar = *curr; currchar != '\0'; currchar = *(++curr)) {
    1.15 -        if ((currchar >= '0' && currchar <= '9') ||
    1.16 -            (currchar >= 'a' && currchar <= 'f') ||
    1.17 -            (currchar >= 'A' && currchar <= 'F'))
    1.18 -        {
    1.19 -            continue;
    1.20 -        }
    1.21 -        return false;
    1.22 -    }
    1.23 -    return true;
    1.24 -}
    1.25 -
    1.26 -// Returns, in comparison: 1 if fpr1 > fpr2, 0 if equal, -1 if fpr1 < fpr2
    1.27 -static PEP_STATUS _compare_fprs(const char* fpr1, const char* fpr2, int* comparison) {
    1.28 -
    1.29 -    const int _FULL_FINGERPRINT_LENGTH = 40;
    1.30 -    const int _ASCII_LOWERCASE_OFFSET = 32;
    1.31 -
    1.32 -    size_t fpr1_len = strlen(fpr1);
    1.33 -    size_t fpr2_len = strlen(fpr2);
    1.34 -
    1.35 -    if (fpr1_len != _FULL_FINGERPRINT_LENGTH || fpr2_len != _FULL_FINGERPRINT_LENGTH)
    1.36 -        return PEP_TRUSTWORDS_FPR_WRONG_LENGTH;
    1.37 -
    1.38 -    if (!_is_valid_hex(fpr1) || !_is_valid_hex(fpr2))
    1.39 -        return PEP_ILLEGAL_VALUE;
    1.40 -
    1.41 -    const char* fpr1_curr = fpr1;
    1.42 -    const char* fpr2_curr = fpr2;
    1.43 -
    1.44 -    char current;
    1.45 -
    1.46 -    // Advance past leading zeros.
    1.47 -    for (current = *fpr1_curr; current != '0' && current != '\0'; current = *(++fpr1_curr), fpr1_len--);
    1.48 -    for (current = *fpr2_curr; current != '0' && current != '\0'; current = *(++fpr2_curr), fpr2_len--);
    1.49 -
    1.50 -    if (fpr1_len == fpr2_len) {
    1.51 -        char digit1;
    1.52 -        char digit2;
    1.53 -
    1.54 -        while (fpr1_curr && *fpr1_curr != '\0') {
    1.55 -            digit1 = *fpr1_curr++;
    1.56 -            digit2 = *fpr2_curr++;
    1.57 -
    1.58 -            // Adjust for case-insensitive compare
    1.59 -            if (digit1 >= 'a' && digit1 <= 'f')
    1.60 -                digit1 -= _ASCII_LOWERCASE_OFFSET;
    1.61 -            if (digit2 >= 'a' && digit2 <= 'f')
    1.62 -                digit2 -= _ASCII_LOWERCASE_OFFSET;
    1.63 -
    1.64 -            // We take advantage of the fact that 'a'-'f' are larger
    1.65 -            // integer values in the ASCII table than '0'-'9'.
    1.66 -            // This allows us to compare digits directly.
    1.67 -            if (digit1 > digit2) {
    1.68 -                *comparison = 1;
    1.69 -                return PEP_STATUS_OK;
    1.70 -            } else if (digit1 < digit2) {
    1.71 -                *comparison = -1;
    1.72 -                return PEP_STATUS_OK;
    1.73 -            }
    1.74 -
    1.75 -            // pointers already advanced above. Keep going.
    1.76 -        }
    1.77 -        *comparison = 0;
    1.78 -        return PEP_STATUS_OK;
    1.79 -    }
    1.80 -    else if (fpr1_len > fpr2_len) {
    1.81 -        *comparison = 1;
    1.82 -        return PEP_STATUS_OK;
    1.83 -    }
    1.84 -    // Otherwise, fpr1_len < fpr2_len
    1.85 -    *comparison = -1;
    1.86 -    return PEP_STATUS_OK;
    1.87 -}
    1.88 -
    1.89  DYNAMIC_API PEP_STATUS get_trustwords(
    1.90      PEP_SESSION session, const pEp_identity* id1, const pEp_identity* id2,
    1.91      const char* lang, char **words, size_t *wsize, bool full
    1.92 @@ -2361,7 +2279,7 @@
    1.93      size_t second_wsize = 0;
    1.94  
    1.95      int fpr_comparison = -255;
    1.96 -    PEP_STATUS status = _compare_fprs(source1, source2, &fpr_comparison);
    1.97 +    PEP_STATUS status = _compare_fprs(source1, strlen(source1), source2, strlen(source2), &fpr_comparison);
    1.98      if (status != PEP_STATUS_OK)
    1.99          return status;
   1.100  
     2.1 --- a/src/pEp_internal.h	Tue May 02 09:32:38 2017 +0200
     2.2 +++ b/src/pEp_internal.h	Wed May 03 18:11:49 2017 +0200
     2.3 @@ -189,7 +189,109 @@
     2.4  }
     2.5  #endif
     2.6  
     2.7 +typedef enum _normalize_hex_rest_t {
     2.8 +    accept_hex,
     2.9 +    ignore_hex,
    2.10 +    reject_hex
    2.11 +} normalize_hex_res_t;
    2.12 +
    2.13 +static inline normalize_hex_res_t _normalize_hex(char *hex) 
    2.14 +{
    2.15 +    if (*hex >= '0' && *hex <= '9')
    2.16 +        return accept_hex;
    2.17 +
    2.18 +    if (*hex >= 'A' && *hex <= 'F') {
    2.19 +        *hex += 'a' - 'A';
    2.20 +        return accept_hex;
    2.21 +    }
    2.22 +
    2.23 +    if (*hex >= 'a' && *hex <= 'f') 
    2.24 +        return accept_hex;
    2.25 +
    2.26 +    if (*hex == ' ') 
    2.27 +        return ignore_hex;
    2.28 +
    2.29 +    return reject_hex;
    2.30 +}
    2.31 +
    2.32  // Space tolerant and case insensitive fingerprint string compare
    2.33 +static inline PEP_STATUS _compare_fprs(
    2.34 +        const char* fpra,
    2.35 +        size_t fpras,
    2.36 +        const char* fprb,
    2.37 +        size_t fprbs,
    2.38 +        int* comparison)
    2.39 +{
    2.40 +
    2.41 +    size_t ai = 0;
    2.42 +    size_t bi = 0;
    2.43 +    size_t significant = 0;
    2.44 +    int _comparison = 0;
    2.45 +    const int _FULL_FINGERPRINT_LENGTH = 40;
    2.46 +   
    2.47 +    // First compare every non-ignored chars until an end is reached
    2.48 +    while(ai < fpras && bi < fprbs)
    2.49 +    {
    2.50 +        char fprac = fpra[ai];
    2.51 +        char fprbc = fprb[bi];
    2.52 +        normalize_hex_res_t fprah = _normalize_hex(&fprac);
    2.53 +        normalize_hex_res_t fprbh = _normalize_hex(&fprbc);
    2.54 +
    2.55 +        if(fprah == reject_hex || fprbh == reject_hex)
    2.56 +            return PEP_ILLEGAL_VALUE;
    2.57 +
    2.58 +        if ( fprah == ignore_hex )
    2.59 +        {
    2.60 +            ai++;
    2.61 +        }
    2.62 +        else if ( fprbh == ignore_hex )
    2.63 +        {
    2.64 +            bi++;
    2.65 +        }
    2.66 +        else
    2.67 +        {
    2.68 +            if(fprac != fprbc && _comparison == 0 )
    2.69 +            {
    2.70 +                _comparison = fprac > fprbc ? 1 : -1;
    2.71 +            }
    2.72 +
    2.73 +            significant++;
    2.74 +            ai++;
    2.75 +            bi++;
    2.76 +
    2.77 +        } 
    2.78 +    }
    2.79 +
    2.80 +    // Bail out if we didn't got enough significnt chars
    2.81 +    if (significant != _FULL_FINGERPRINT_LENGTH )
    2.82 +        return PEP_TRUSTWORDS_FPR_WRONG_LENGTH;
    2.83 +
    2.84 +    // Then purge remaining chars, all must be ignored chars
    2.85 +    while ( ai < fpras )
    2.86 +    {
    2.87 +        char fprac = fpra[ai];
    2.88 +        normalize_hex_res_t fprah = _normalize_hex(&fprac);
    2.89 +        if( fprah == reject_hex )
    2.90 +            return PEP_ILLEGAL_VALUE;
    2.91 +        if ( fprah != ignore_hex )
    2.92 +            return PEP_TRUSTWORDS_FPR_WRONG_LENGTH;
    2.93 +        ai++;
    2.94 +    }
    2.95 +    while ( bi < fprbs )
    2.96 +    {
    2.97 +        char fprbc = fprb[bi];
    2.98 +        normalize_hex_res_t fprbh = _normalize_hex(&fprbc);
    2.99 +        if( fprbh == reject_hex )
   2.100 +            return PEP_ILLEGAL_VALUE;
   2.101 +        if ( fprbh != ignore_hex )
   2.102 +            return PEP_TRUSTWORDS_FPR_WRONG_LENGTH;
   2.103 +        bi++;
   2.104 +    }
   2.105 +
   2.106 +    *comparison = _comparison;
   2.107 +    return PEP_STATUS_OK;
   2.108 +}
   2.109 +
   2.110  static inline int _same_fpr(
   2.111          const char* fpra,
   2.112          size_t fpras,
   2.113 @@ -197,35 +299,10 @@
   2.114          size_t fprbs
   2.115      )
   2.116  {
   2.117 -    size_t ai = 0;
   2.118 -    size_t bi = 0;
   2.119 -    
   2.120 -    do
   2.121 -    {
   2.122 -        if(fpra[ai] == 0 || fprb[bi] == 0)
   2.123 -        {
   2.124 -            return 0;
   2.125 -        }
   2.126 -        else if(fpra[ai] == ' ')
   2.127 -        {
   2.128 -            ai++;
   2.129 -        }
   2.130 -        else if(fprb[bi] == ' ')
   2.131 -        {
   2.132 -            bi++;
   2.133 -        }
   2.134 -        else if(toupper(fpra[ai]) == toupper(fprb[bi]))
   2.135 -        {
   2.136 -            ai++;
   2.137 -            bi++;
   2.138 -        }
   2.139 -        else
   2.140 -        {
   2.141 -            return 0;
   2.142 -        }
   2.143 -        
   2.144 -    }
   2.145 -    while(ai < fpras && bi < fprbs);
   2.146 -    
   2.147 -    return ai == fpras && bi == fprbs;
   2.148 +    // illegal values are ignored, and considered not same.
   2.149 +    int comparison = 1;
   2.150 +
   2.151 +    _compare_fprs(fpra, fpras, fprb, fprbs, &comparison);
   2.152 +
   2.153 +    return comparison == 0;
   2.154  }
     3.1 --- a/test/trustwords_test.cc	Tue May 02 09:32:38 2017 +0200
     3.2 +++ b/test/trustwords_test.cc	Wed May 03 18:11:49 2017 +0200
     3.3 @@ -33,6 +33,12 @@
     3.4          "blargh",
     3.5          "Krista Grothoff");
     3.6      
     3.7 +    pEp_identity* identity2_with_spaces = new_identity(
     3.8 +        "krista@kgrothoff.org",
     3.9 +        " 62D4932086185C159 17B72D30571A FBCA    5493553   ",
    3.10 +        "blargh",
    3.11 +        "Krista Grothoff");
    3.12 +    
    3.13      string fingerprint1 = identity1->fpr;
    3.14      string fingerprint2 = identity2->fpr;
    3.15      char* words1 = nullptr;
    3.16 @@ -58,6 +64,11 @@
    3.17      get_trustwords(session, identity1, identity2, "de", &full_wordlist, &wsize_full, false);
    3.18      assert(full_wordlist);
    3.19      cout << full_wordlist << "\n";
    3.20 +
    3.21 +    cout << "\nfinding German trustwords for " << identity1->address << " and " << identity2->address << "...\n";
    3.22 +    get_trustwords(session, identity1, identity2_with_spaces, "de", &full_wordlist, &wsize_full, false);
    3.23 +    assert(full_wordlist);
    3.24 +    cout << full_wordlist << "\n";
    3.25      
    3.26      
    3.27      pEp_free(words1);
    3.28 @@ -79,6 +90,11 @@
    3.29      assert(full_wordlist);
    3.30      cout << full_wordlist << "\n";
    3.31  
    3.32 +    cout << "\nfinding French trustwords for " << identity2->address << " and " << identity2->address << "...\n";
    3.33 +    get_trustwords(session, identity2, identity2_with_spaces, "fr", &full_wordlist, &wsize_full, false);
    3.34 +    assert(full_wordlist);
    3.35 +    cout << full_wordlist << "\n";
    3.36 +
    3.37      pEp_free(words1);
    3.38      words1 = nullptr;
    3.39      pEp_free(full_wordlist);
    3.40 @@ -101,6 +117,11 @@
    3.41      assert(full_wordlist);
    3.42      cout << full_wordlist << "\n";
    3.43      
    3.44 +    cout << "\nfinding English trustwords for " << identity2->address << " and " << identity2->address << "...\n";
    3.45 +    get_trustwords(session, identity2_with_spaces, identity1, "en", &full_wordlist, &wsize_full, true);
    3.46 +    assert(full_wordlist);
    3.47 +    cout << full_wordlist << "\n";
    3.48 +    
    3.49      pEp_free(words1);
    3.50      words1 = nullptr;
    3.51      pEp_free(words2);