【问题标题】:Getting a 128 bits integer from command line从命令行获取 128 位整数
【发布时间】:2018-07-20 19:50:42
【问题描述】:

我正在尝试使用 unsigned long long 密钥来执行 Tiny Encryption Algorithm 算法。

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char** argv){
    unsigned int key[4] = { 0 };
    *key = strtoll(argv[1], NULL, 10);
    printf("%s key = %llu\n", argv[0], key);
    return 0;
}

这是我的输入:

./a.out 9223372036854775700

这是输出:

./a.out key = 140723741574976

所以我在 argv[1] 中传递了一个 128 位密钥。它不应该在内存中正确地转换到unsigned int 数组中吗?

所以,我想弄清楚为什么这是我的程序的输出。这与字节顺序有关吗?

【问题讨论】:

  • 当你编写代码时,你需要你的类型有一些精确的大小,你最好使用更具体的类型,比如__int128(假设你使用 GCC,它支持),因为它不是系统相关
  • 我无法控制 strtoll() 函数的返回值。将其转换为 __int128 是没用的,对吧?
  • 您的代码存在严重问题。一,printf("%s key = %llu\n", argv[0], key); 发出key 数组的地址,但%llu 格式说明符不正确,这是未定义的行为。第二,如果keyint 数组,*key 指的是int 值,而不是long long 值。您只是将数据存储到数组的 first 成员中。而且您似乎没有#include &lt;stdlib.h&gt; 行,因此生成的strtoll() 隐式声明意味着编译器假定它返回int
  • 好吧,我怎样才能从argv[1] 将一个 128 位整数存储在一个无符号长数组中?
  • 长话短说:C 并不真正支持大于 64 位的整数。您需要使用“big int”或“bignum”库,就像在许多加密库中一样。

标签: c arrays pointers bit-manipulation 128-bit


【解决方案1】:

long long 仅指定包含至少 64 位。您最好将密钥作为十六进制传递并手动将其解析为字节数组

【讨论】:

    【解决方案2】:

    为什么不手动呢?获取__int128 类型变量,遍历输入的每个数字并将其插入到变量中:

    int main(int argc, char** argv){
    
        __int128 key = 0;
        int i;
        for (i=0; i<strlen(argv[1]); i++){
    
            key *= 10; // "shift" current value to make space for adding one more decimal
            key += argv[1][i] - '0'; // convert ascii character to number 
    
        }
    
        printf("%s key = %llu\n", argv[0], key);
        return 0;
    }
    

    请注意,如果argv[1] 太长,密钥将丢弃它的第一个数字而不是最后一个数字。所以,也许这也是你需要注意的事情,根据你的喜好

    【讨论】:

      【解决方案3】:

      代码试图打印数组key[0] 的地址而不是其值。这不是字节序问题。启用所有编译器警告以节省时间。

      *key = strtoll(argv[1], NULL, 10); 尝试将 long long(至少 64 位)保存到 unsigned int,这可能只有 32 个。

      字符串“9223372036854775700”表示一个 63 位的数字。

      首先尝试使用至少是 64 位数字的unsigned long long

      int main(int argc, char** argv){
          // unsigned int key[4] = { 0 };
          unsigned long long  key = strtoull(argv[1], NULL, 10);
          printf("%s key = %llu\n", argv[0], key);
          return 0;
      }
      

      C 没有指定对 128 位整数的支持。可以编写用户代码来解决这个问题。 @C_Elegans 使用十六进制文本的想法很好。

      由于int 可以有多种尺寸,更好用

      #include <stdint.h>
      
      // unsigned int key[4];
      uint32_t key[4];
      

      一个示例代码想法

      #include <ctype.h>
      #include <errno.h>
      #include <inttypes.h>
      #include <stdint.h>
      #include <stdlib.h>
      
      typedef struct {
        uint16_t u[8];
      } my_uint128_t;
      
      my_uint128_t strtomy_uint128(const char *s, char **endptr, int base) {
        my_uint128_t y = {0};
        while (isalnum((unsigned char ) *s)) {
          char *endptr;
          uint32_t sum = (uint32_t) strtoul((char[2]) {*s, '\0'}, &endptr, base);
          if (*endptr) {
            break;
          }
          for (int i = 0; i < 8; i++) {
            sum +=  y.u[i] * (uint32_t) base;
            y.u[i] = (uint16_t) sum;
            sum >>= 16;
          }
          if (sum) {
            errno = ERANGE;
            for (int i = 0; i < 8; i++) {
              y.u[i] = UINT16_MAX;
            }
          }
          s++;
        }
        if (endptr) {
          *endptr = (char *) s;
        }
        return y;
      }
      
      void uint128_dump(my_uint128_t x) {
        for (int i = 8; i > 0; ) {
          i--;
          printf("%04" PRIX16 "%c", x.u[i], i ? ' ' : '\n');
        }
      }
      
      int main(void) {
        my_uint128_t a = strtomy_uint128("9223372036854775700", 0, 10);
        uint128_dump(a);
      }
      

      输出

      0000 0000 0000 0000 7FFF FFFF FFFF FF94
      

      【讨论】:

        【解决方案4】:

        退后一步,看看你想要实现什么。 Tiny 加密算法不适用于 128 位整数,但适用于 128 位密钥;密钥由四个 32 位无符号整数组成。

        您真正需要的是一种将十进制(或十六进制,或其他基数)128 位无符号整数从字符串解析为四个 32 位无符号整数元素的方法。

        我建议编写一个乘加函数,它采用四 32 位值,将其乘以一个 32 位常数,然后再加上另一个 32 位常数:

        #include <stdint.h>
        
        uint32_t muladd128(uint32_t quad[4], const uint32_t mul, const uint32_t add)
        {
            uint64_t  temp = 0;
        
            temp = (uint64_t)quad[3] * (uint64_t)mul + add;
            quad[3] = temp;
        
            temp = (uint64_t)quad[2] * (uint64_t)mul + (temp >> 32);
            quad[2] = temp;
        
            temp = (uint64_t)quad[1] * (uint64_t)mul + (temp >> 32);
            quad[1] = temp;
        
            temp = (uint64_t)quad[0] * (uint64_t)mul + (temp >> 32);
            quad[0] = temp;
        
            return temp >> 32;
        }
        

        以上使用最重要的第一个词序。如果结果溢出,则返回非零;实际上,它返回的是 32 位溢出本身。

        这样,很容易解析描述二进制、八进制、十进制或十六进制的非负 128 位整数的字符串:

        #include <stdlib.h>
        #include <stdint.h>
        #include <string.h>
        #include <stdio.h>
        #include <errno.h>
        
        static void clear128(uint32_t quad[4])
        {
            quad[0] = quad[1] = quad[2] = quad[3] = 0;
        }
        
        /* muladd128() */
        
        static const char *parse128(uint32_t quad[4], const char *from)
        {
            if (!from) {
                errno = EINVAL;
                return NULL;
            }
        
            while (*from == '\t' || *from == '\n' || *from == '\v' ||
                   *from == '\f' || *from == '\r' || *from == ' ')
                from++;
        
            if (from[0] == '0' && (from[1] == 'x' || from[1] == 'X') &&
                ((from[2] >= '0' && from[2] <= '9') ||
                 (from[2] >= 'A' && from[2] <= 'F') ||
                 (from[2] >= 'a' && from[2] <= 'f'))) {
                /* Hexadecimal */
                from += 2;
                clear128(quad);
        
                while (1)
                    if (*from >= '0' && *from <= '9') {
                        if (muladd128(quad, 16, *from - '0')) {
                            errno = ERANGE;
                            return NULL;
                        }
                        from++;
                    } else
                    if (*from >= 'A' && *from <= 'F') {
                        if (muladd128(quad, 16, *from - 'A' + 10)) {
                            errno = ERANGE;
                            return NULL;
                        }
                        from++;
                    } else
                    if (*from >= 'a' && *from <= 'f') {
                        if (muladd128(quad, 16, *from - 'a' + 10)) {
                            errno = ERANGE;
                            return NULL;
                        }
                        from++;
                    } else
                        return from;
            }
        
            if (from[0] == '0' && (from[1] == 'b' || from[1] == 'B') &&
                (from[2] >= '0' && from[2] <= '1')) {
                /* Binary */
                from += 2;
                clear128(quad);
        
                while (1)
                    if (*from >= '0' && *from <= '1') {
                        if (muladd128(quad, 2, *from - '0')) {
                            errno = ERANGE;
                            return NULL;
                        }
                        from++;
                    } else
                        return from;
            }
        
            if (from[0] == '0' &&
                (from[1] >= '0' && from[1] <= '7')) {
                /* Octal */
                from += 1;
                clear128(quad);
        
                while (1)
                    if (*from >= '0' && *from <= '7') {
                        if (muladd128(quad, 8, *from - '0')) {
                            errno = ERANGE;
                            return NULL;
                        }
                        from++;
                    } else
                        return from;
            }
        
            if (from[0] >= '0' && from[0] <= '9') {
                /* Decimal */
                clear128(quad);
        
                while (1)
                    if (*from >= '0' && *from <= '9') {
                        if (muladd128(quad, 10, *from - '0')) {
                            errno = ERANGE;
                            return NULL;
                        }
                        from++;
                    } else
                        return from;
            }
        
            /* Not a recognized number. */
            errno = EINVAL;
            return NULL;
        }
        
        int main(int argc, char *argv[])
        {
            uint32_t key[4];
            int      arg;
        
            for (arg = 1; arg < argc; arg++) {
                const char *end = parse128(key, argv[arg]);
                if (end) {
                    if (*end != '\0')
                        printf("%s: 0x%08x%08x%08x%08x (+ \"%s\")\n", argv[arg], key[0], key[1], key[2], key[3], end);
                    else
                        printf("%s: 0x%08x%08x%08x%08x\n", argv[arg], key[0], key[1], key[2], key[3]);
                    fflush(stdout);
                } else {
                    switch (errno) {
                    case ERANGE:
                        fprintf(stderr, "%s: Too large.\n", argv[arg]);
                        break;
                    case EINVAL:
                        fprintf(stderr, "%s: Not a nonnegative integer in binary, octal, decimal, or hexadecimal notation.\n", argv[arg]);
                        break;
                    default:
                        fprintf(stderr, "%s: %s.\n", argv[arg], strerror(errno));
                        break;
                    }
                }
            }
        
            return EXIT_SUCCESS;
        }
        

        添加对有时使用的 Base64 和 Base85 的支持非常简单;或者实际上对于任何小于 232 的基数。

        而且,如果您考虑以上内容,一切都取决于您需要准确无误。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-01-03
          • 1970-01-01
          • 2015-11-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-03-21
          • 1970-01-01
          相关资源
          最近更新 更多