1

我正在尝试使用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

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

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

4

4 回答 4

3

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

于 2018-02-10T00:44:17.947 回答
2

退后一步,看看你想要实现什么。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 的支持非常简单;或者实际上对于任何小于 2 32的基数。

而且,如果您考虑上述情况,这完全取决于您需要什么。

于 2018-02-10T14:58:42.817 回答
1

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

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

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

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

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
于 2018-02-10T03:38:39.983 回答
0

为什么不手动做呢?获取一个__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]太长,密钥将丢弃它的第一个数字而不是最后一个数字。所以,也许这也是你需要照顾的事情,根据你的喜好

于 2018-02-10T01:12:10.257 回答