/**************************************************************************** * libs/libc/unistd/lib_crypt_r.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #define KEY_MAX 30000 #define SALT_MAX 8 /**************************************************************************** * Private Types ****************************************************************************/ typedef struct cryptodev_context_s { int fd; struct session_op session; struct crypt_op crypt; }cryptodev_context_t; /**************************************************************************** * Private Data ****************************************************************************/ /* 0 ... 63 => ascii - 64 */ static const unsigned char g_md5_itoa64[] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; /**************************************************************************** * Private Functions ****************************************************************************/ static FAR char *md5_to64(FAR char *s, unsigned long v, int n) { while (--n >= 0) { *s++ = g_md5_itoa64[v & 0x3f]; v >>= 6; } return s; } static int md5_init(FAR cryptodev_context_t *ctx) { int ret; int fd; memset(ctx, 0, sizeof(cryptodev_context_t)); fd = open("/dev/crypto", O_RDWR, 0); if (fd < 0) { return -get_errno(); } ret = ioctl(fd, CRIOGET, &ctx->fd); close(fd); if (ret < 0) { return -get_errno(); } ctx->session.mac = CRYPTO_MD5; ret = ioctl(ctx->fd, CIOCGSESSION, &ctx->session); if (ret < 0) { return -get_errno(); } ctx->crypt.ses = ctx->session.ses; return ret; } static int md5_update(FAR cryptodev_context_t *ctx, FAR const void *input, size_t ilen) { int ret; ctx->crypt.op = COP_ENCRYPT; ctx->crypt.flags |= COP_FLAG_UPDATE; ctx->crypt.src = (caddr_t)input; ctx->crypt.len = ilen; ret = ioctl(ctx->fd, CIOCCRYPT, &ctx->crypt); return ret < 0 ? -get_errno() : ret; } static int md5_finish(FAR cryptodev_context_t *ctx, unsigned char output[16]) { int ret; ctx->crypt.op = COP_ENCRYPT; ctx->crypt.flags = 0; ctx->crypt.mac = (caddr_t)output; ret = ioctl(ctx->fd, CIOCCRYPT, &ctx->crypt); if (ret < 0) { return -get_errno(); } ioctl(ctx->fd, CIOCFSESSION, &ctx->session.ses); ctx->crypt.ses = 0; close(ctx->fd); memset(ctx, 0, sizeof(cryptodev_context_t)); return ret; } /* UNIX password * Use MD5 for what it is best at... */ static FAR char *md5_crypt(FAR const char *key, FAR const char *setting, FAR char *output) { cryptodev_context_t ctx; unsigned char md[16]; unsigned int i; unsigned int klen; unsigned int slen; FAR const char *salt; FAR char *p; static const unsigned char perm[][3] = { { 0, 6, 12 }, { 1, 7, 13 }, { 2, 8, 14 }, { 3, 9, 15 }, { 4, 10, 5 } }; /* Reject large keys */ klen = strnlen(key, KEY_MAX + 1); if (klen > KEY_MAX) { return 0; } /* Setting: $1$salt$ (closing $ is optional) */ salt = setting + 3; for (i = 0; i < SALT_MAX && salt[i] && salt[i] != '$'; i++); slen = i; /* Md5(key salt key) */ md5_init(&ctx); md5_update(&ctx, key, klen); md5_update(&ctx, salt, slen); md5_update(&ctx, key, klen); md5_finish(&ctx, md); /* Md5(key $1$ salt repeated-md weird-key[0]-0) */ md5_init(&ctx); md5_update(&ctx, key, klen); md5_update(&ctx, setting, 3 + slen); for (i = klen; i > sizeof md; i -= sizeof md) { md5_update(&ctx, md, sizeof md); } md5_update(&ctx, md, i); md[0] = 0; for (i = klen; i; i >>= 1) { if (i & 1) { md5_update(&ctx, md, 1); } else { md5_update(&ctx, key, 1); } } md5_finish(&ctx, md); for (i = 0; i < 1000; i++) { md5_init(&ctx); if (i % 2) { md5_update(&ctx, key, klen); } else { md5_update(&ctx, md, sizeof md); } if (i % 3) { md5_update(&ctx, salt, slen); } if (i % 7) { md5_update(&ctx, key, klen); } if (i % 2) { md5_update(&ctx, md, sizeof md); } else { md5_update(&ctx, key, klen); } md5_finish(&ctx, md); } /* Output is $1$salt$hash */ memcpy(output, setting, 3 + slen); p = output + 3 + slen; *p++ = '$'; for (i = 0; i < 5; i++) { p = md5_to64(p, (md[perm[i][0]] << 16) | (md[perm[i][1]] << 8) | md[perm[i][2]], 4); } p = md5_to64(p, md[11], 2); *p = 0; return output; } /**************************************************************************** * Public Functions ****************************************************************************/ FAR char *crypt_r(FAR const char *key, FAR const char *salt, FAR char *output) { /* First, check if we are supposed to be using the MD5 * not support DES... */ if (salt[0] == '$' && salt[1] == '1' && salt[2] == '$') { return md5_crypt(key, salt, output); } else { return NULL; } }