static char * build_A2_hash(heim_digest_t context, const char *method) { unsigned char md[CC_MD5_DIGEST_LENGTH]; CC_MD5_CTX ctx; char *A2; CC_MD5_Init(&ctx); if (method) CC_MD5_Update(&ctx, method, (CC_LONG)strlen(method)); CC_MD5_Update(&ctx, ":", 1); CC_MD5_Update(&ctx, context->clientURI, (CC_LONG)strlen(context->clientURI)); /* conf|int */ if (context->type == HEIM_DIGEST_TYPE_RFC2831) { if (strcasecmp(context->clientQOP, "auth-int") == 0 || strcasecmp(context->clientQOP, "auth-conf") == 0) { /* XXX if we have a body hash, use that */ static char conf_zeros[] = ":00000000000000000000000000000000"; CC_MD5_Update(&ctx, conf_zeros, sizeof(conf_zeros) - 1); } } else { /* support auth-int ? */ if (context->clientQOP && strcasecmp(context->clientQOP, "auth") != 0) return NULL; } CC_MD5_Final(md, &ctx); hex_encode(md, sizeof(md), &A2); if (A2) strlwr(A2); return A2; }
static void digest_userhash(const char *user, const char *realm, const char *password, unsigned char md[CC_MD5_DIGEST_LENGTH]) { CC_MD5_CTX ctx; CC_MD5_Init(&ctx); CC_MD5_Update(&ctx, user, (CC_LONG)strlen(user)); CC_MD5_Update(&ctx, ":", 1); CC_MD5_Update(&ctx, realm, (CC_LONG)strlen(realm)); CC_MD5_Update(&ctx, ":", 1); CC_MD5_Update(&ctx, password, (CC_LONG)strlen(password)); CC_MD5_Final(md, &ctx); }
CFDataRef WebApiClientMD5DigestCreateWithFilePath(CFStringRef filePath, size_t bufferSize) { // Declare needed variables CFDataRef result = NULL; CFReadStreamRef readStream = NULL; // Get the file URL CFURLRef fileURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (CFStringRef)filePath, kCFURLPOSIXPathStyle, (Boolean)false); if ( fileURL ) { // Create and open the read stream readStream = CFReadStreamCreateWithFile(kCFAllocatorDefault, fileURL); if ( readStream ) { Boolean didSucceed = CFReadStreamOpen(readStream); if ( didSucceed ) { // Initialize the hash object CC_MD5_CTX hashObject; CC_MD5_Init(&hashObject); // Make sure chunkSizeForReadingData is valid if ( bufferSize < 1 ) { bufferSize = FileHashDefaultChunkSizeForReadingData; } // Feed the data to the hash object bool hasMoreData = true; while ( hasMoreData ) { uint8_t buffer[bufferSize]; CFIndex readBytesCount = CFReadStreamRead(readStream, (UInt8 *)buffer, (CFIndex)sizeof(buffer)); if ( readBytesCount == -1 ) break; if ( readBytesCount == 0 ) { hasMoreData = false; continue; } CC_MD5_Update(&hashObject, (const void *)buffer, (CC_LONG)readBytesCount); } // Check if the read operation succeeded didSucceed = !hasMoreData; // Compute the hash digest unsigned char digest[CC_MD5_DIGEST_LENGTH]; CC_MD5_Final(digest, &hashObject); // Abort if the read operation failed if ( didSucceed ) { result = CFDataCreate(kCFAllocatorDefault, digest, CC_MD5_DIGEST_LENGTH); } } } } if ( readStream ) { CFReadStreamClose(readStream); CFRelease(readStream); } if ( fileURL ) { CFRelease(fileURL); } return result; }
HashResult MD5CommonCryptoImpl::Calculate(Aws::IStream& stream) { CC_MD5_CTX md5; CC_MD5_Init(&md5); auto currentPos = stream.tellg(); stream.seekg(0, stream.beg); char streamBuffer[Aws::Utils::Crypto::Hash::INTERNAL_HASH_STREAM_BUFFER_SIZE]; while(stream.good()) { stream.read(streamBuffer, Aws::Utils::Crypto::Hash::INTERNAL_HASH_STREAM_BUFFER_SIZE); auto bytesRead = stream.gcount(); if(bytesRead > 0) { CC_MD5_Update(&md5, streamBuffer, static_cast<CC_LONG>(bytesRead)); } } stream.clear(); stream.seekg(currentPos, stream.beg); ByteBuffer hash(CC_MD5_DIGEST_LENGTH); CC_MD5_Final(hash.GetUnderlyingData(), &md5); return HashResult(std::move(hash)); }
static boolean_t should_update_icon(FILE *current_icon) { struct stat sbuf; off_t current_icon_size; CC_MD5_CTX ctx; size_t n; unsigned char buf[1024]; unsigned char current_icon_md[CC_MD5_DIGEST_LENGTH]; int i; fstat(fileno(current_icon), &sbuf); current_icon_size = sbuf.st_size; if (current_icon_size != snowflake_icon_size) { return (B_FALSE); } CC_MD5_Init(&ctx); while ((n = fread(buf, 1, 1024, current_icon)) > 0) { CC_MD5_Update(&ctx, buf, (CC_LONG)n); } CC_MD5_Final(current_icon_md, &ctx); rewind(current_icon); for(i = 0; i < CC_MD5_DIGEST_LENGTH; i++) { if (current_icon_md[i] != snowflake_icon_md[i]) { return (B_FALSE); } } return (B_TRUE); }
static int __archive_libsystem_md5update(archive_md5_ctx *ctx, const void *indata, size_t insize) { CC_MD5_Update(ctx, indata, insize); return (ARCHIVE_OK); }
extern "C" int32_t AppleCryptoNative_DigestUpdate(DigestCtx* ctx, uint8_t* pBuf, int32_t cbBuf) { if (cbBuf == 0) return 1; if (ctx == nullptr || pBuf == nullptr) return -1; CC_LONG bufSize = static_cast<CC_LONG>(cbBuf); switch (ctx->algorithm) { case PAL_MD5: return CC_MD5_Update(&ctx->d.md5, pBuf, bufSize); case PAL_SHA1: return CC_SHA1_Update(&ctx->d.sha1, pBuf, bufSize); case PAL_SHA256: return CC_SHA256_Update(&ctx->d.sha256, pBuf, bufSize); case PAL_SHA384: return CC_SHA384_Update(&ctx->d.sha384, pBuf, bufSize); case PAL_SHA512: return CC_SHA512_Update(&ctx->d.sha512, pBuf, bufSize); default: return -1; } }
static OSStatus HashMD5Update(SSLBuffer *digestCtx, const SSLBuffer *data) { /* 64 bits cast: safe, SSL records are always smaller than 2^32 bytes */ assert(digestCtx->length >= sizeof(CC_MD5_CTX)); CC_MD5_CTX *ctx = (CC_MD5_CTX *)digestCtx->data; CC_MD5_Update(ctx, data->data, (CC_LONG)data->length); return noErr; }
void MD5Object::digestUpdate( const void *data, size_t len) { if(mIsDone) { throw std::runtime_error("MD5 digestUpdate after final"); } CC_MD5_Update(&mCtx, data, len); }
static void _wi_md5_ctx_update(_wi_md5_ctx_t *ctx, const void *data, unsigned long length) { #ifdef WI_MD5_OPENSSL MD5_Update(&ctx->openssl_ctx, data, length); #endif #ifdef WI_MD5_COMMONCRYPTO CC_MD5_Update(&ctx->commondigest_ctx, data, length); #endif }
void crypt_md5_hash(const void* content, size_t content_size, void* md5, size_t md5_size) { assert(md5_size >= 16); CC_MD5_CTX context; CC_MD5_Init(&context); CC_MD5_Update(&context, content, (CC_LONG)content_size); CC_MD5_Final(md5, &context); }
void ocspdMD5( const void *data, CC_LONG len, unsigned char *md) // allocd by caller, CC_MD5_DIGEST_LENGTH bytes { CC_MD5_CTX ctx; CC_MD5_Init(&ctx); CC_MD5_Update(&ctx, data, len); CC_MD5_Final(md, &ctx); }
void cCCHmacFinal(CCHmacContext *ctx, void *m) { size_t i; unsigned char md5[CC_MD5_DIGEST_LENGTH]; /* 4. Compute MD5 of result of step 3 */ CC_MD5_Final(md5, &ctx->md5ctx); /* 5. XOR step 1 key with 0x5c */ for (i=0; i<CC_MD5_BLOCK_BYTES; ++i) ctx->keydata[i] ^= (0x36 ^ 0x5c); /* 6. Append MD5 result from step 4 to result of step 5 */ /* 7. Compute MD5 of result of step 6 and output the result */ CC_MD5_Init(&ctx->md5ctx); CC_MD5_Update(&ctx->md5ctx, ctx->keydata, CC_MD5_BLOCK_BYTES); CC_MD5_Update(&ctx->md5ctx, md5, CC_MD5_DIGEST_LENGTH); CC_MD5_Final(m, &ctx->md5ctx); }
void Crypto::DigestUpdate(Digest x, const StringPiece &in) { auto d = FromVoid<CCDigest*>(x); switch(d->algo) { case CCDigestAlgo::MD5: CC_MD5_Update (static_cast<CC_MD5_CTX*> (d->v), in.data(), in.size()); break; case CCDigestAlgo::SHA1: CC_SHA1_Update (static_cast<CC_SHA1_CTX*> (d->v), in.data(), in.size()); break; case CCDigestAlgo::SHA256: CC_SHA256_Update(static_cast<CC_SHA256_CTX*>(d->v), in.data(), in.size()); break; case CCDigestAlgo::SHA384: CC_SHA384_Update(static_cast<CC_SHA512_CTX*>(d->v), in.data(), in.size()); break; case CCDigestAlgo::SHA512: CC_SHA512_Update(static_cast<CC_SHA512_CTX*>(d->v), in.data(), in.size()); break; default: break; } }
static int file_compare(FILE *f1, FILE *f2) { struct stat sbuf; off_t f1size, f2size; CC_MD5_CTX ctx; size_t n; unsigned char buf[1024]; unsigned char mdresult1[CC_MD5_DIGEST_LENGTH]; unsigned char mdresult2[CC_MD5_DIGEST_LENGTH]; int i; fstat(fileno(f1), &sbuf); f1size = sbuf.st_size; fstat(fileno(f2), &sbuf); f2size = sbuf.st_size; if (f1size != f2size) return (-1); CC_MD5_Init(&ctx); while ((n = fread(buf, 1, 1024, f1)) > 0) { CC_MD5_Update(&ctx, buf, (CC_LONG)n); } CC_MD5_Final(mdresult1, &ctx); rewind(f1); CC_MD5_Init(&ctx); while ((n = fread(buf, 1, 1024, f2)) > 0) { CC_MD5_Update(&ctx, buf, (CC_LONG)n); } CC_MD5_Final(mdresult2, &ctx); rewind(f2); for(i = 0; i < CC_MD5_DIGEST_LENGTH; i++) { if (mdresult1[i] != mdresult2[i]) return (-1); } return (0); }
/** * @brief 終了化処理 * @param [out]aDigest MD5ダイジェスト値 * @param [in]apContext コンテキスト * @retval なし * @note MD5ダイジェストを取得する */ void CC_MD5_Final(UINT8 *aDigest, T_CC_CMN_MD5_CTX *apContext) { UINT8 bits[8]; UINT32 index; UINT32 padLen; /* Save number of bits */ Encode(bits, apContext->count, 8); /* Pad out to 56 mod 64. */ index = (UINT32)((apContext->count[0] >> 3) & 0x3f); padLen = (index < 56) ? (56 - index) : (120 - index); CC_MD5_Update(apContext, PADDING, padLen); /* Append length (before padding) */ CC_MD5_Update(apContext, bits, 8); /* Store state in digest */ Encode(aDigest, apContext->state, 16); memset((UINT8 *)apContext, 0, sizeof(*apContext)); }
static krb5_error_code k5_md5_hash(unsigned int icount, const krb5_data *input, krb5_data *output) { CC_MD5_CTX ctx; unsigned int i; if (output->length != CC_MD5_DIGEST_LENGTH) return(KRB5_CRYPTO_INTERNAL); CC_MD5_Init(&ctx); for (i=0; i<icount; i++) CC_MD5_Update(&ctx, (unsigned char *) input[i].data, input[i].length); CC_MD5_Final((unsigned char *)output->data, &ctx); return(0); }
static void lib_md_update(mrb_state *mrb, struct mrb_md *md, unsigned char *data, mrb_int len) { if (sizeof(len) > sizeof(CC_LONG)) { if (len != (CC_LONG)len) { mrb_raise(mrb, E_ARGUMENT_ERROR, "too long string (not supported yet)"); } } switch (md->type) { case MD_TYPE_MD5: CC_MD5_Update(md->ctx, data, len); break; case MD_TYPE_SHA1: CC_SHA1_Update(md->ctx, data, len); break; case MD_TYPE_SHA256: CC_SHA256_Update(md->ctx, data, len); break; case MD_TYPE_SHA384: CC_SHA384_Update(md->ctx, data, len); break; case MD_TYPE_SHA512: CC_SHA512_Update(md->ctx, data, len); break; default: break; } }
static char * build_A1_hash(heim_digest_t context) { unsigned char md[CC_MD5_DIGEST_LENGTH]; CC_MD5_CTX ctx; char *A1; if (context->flags & F_HAVE_HA1) { memcpy(md, context->SecretHash, sizeof(md)); } else if (context->flags & F_HAVE_HASH) { memcpy(md, context->SecretHash, sizeof(md)); } else if (context->password) { if (context->clientUsername == NULL) return NULL; if (context->serverRealm == NULL) return NULL; digest_userhash(context->clientUsername, context->serverRealm, context->password, md); } else return NULL; if ((context->type == HEIM_DIGEST_TYPE_RFC2617_MD5_SESS || context->type == HEIM_DIGEST_TYPE_RFC2831) && (context->flags & F_HAVE_HA1) == 0) { if (context->serverNonce == NULL) return NULL; CC_MD5_Init(&ctx); CC_MD5_Update(&ctx, md, sizeof(md)); memset(md, 0, sizeof(md)); CC_MD5_Update(&ctx, ":", 1); CC_MD5_Update(&ctx, context->serverNonce, (CC_LONG)strlen(context->serverNonce)); if (context->clientNonce) { CC_MD5_Update(&ctx, ":", 1); CC_MD5_Update(&ctx, context->clientNonce, (CC_LONG)strlen(context->clientNonce)); } if (context->type == HEIM_DIGEST_TYPE_RFC2831 && context->auth_id) { CC_MD5_Update(&ctx, ":", 1); CC_MD5_Update(&ctx, context->auth_id, (CC_LONG)strlen(context->auth_id)); } CC_MD5_Final(md, &ctx); } hex_encode(md, sizeof(md), &A1); if (A1) strlwr(A1); return A1; }
void cCCHmacInit(CCHmacContext *ctx, int alg, const void *k, size_t kl) { size_t i; if (alg != kCCHmacAlgMD5) die("cCCHmacInit requires kCCHmacAlgMD5"); /* 1. Get CC_MD5_BLOCK_BYTES byte key */ if (kl > CC_MD5_BLOCK_BYTES) kl = CC_MD5_BLOCK_BYTES; memcpy(ctx->keydata, k, kl); if (kl < CC_MD5_BLOCK_BYTES) memset(&ctx->keydata[kl], 0, CC_MD5_BLOCK_BYTES - kl); /* 2. XOR step 1 key with 0x36 */ for (i=0; i<CC_MD5_BLOCK_BYTES; ++i) ctx->keydata[i] ^= 0x36; /* 3. Append text to the result of step 2 */ /* 4. Compute MD5 of result of step 3 */ CC_MD5_Init(&ctx->md5ctx); CC_MD5_Update(&ctx->md5ctx, ctx->keydata, CC_MD5_BLOCK_BYTES); }
static char * build_digest(heim_digest_t context, const char *a1, const char *method) { CC_MD5_CTX ctx; uint8_t md[CC_MD5_DIGEST_LENGTH]; char *a2, *str = NULL; a2 = build_A2_hash(context, method); if (a2 == NULL) return NULL; CC_MD5_Init(&ctx); CC_MD5_Update(&ctx, a1, (CC_LONG)strlen(a1)); CC_MD5_Update(&ctx, ":", 1); CC_MD5_Update(&ctx, context->serverNonce, (CC_LONG)strlen(context->serverNonce)); if (context->type != HEIM_DIGEST_TYPE_RFC2069) { CC_MD5_Update(&ctx, ":", 1); CC_MD5_Update(&ctx, context->clientNC, (CC_LONG)strlen(context->clientNC)); CC_MD5_Update(&ctx, ":", 1); CC_MD5_Update(&ctx, context->clientNonce, (CC_LONG)strlen(context->clientNonce)); CC_MD5_Update(&ctx, ":", 1); CC_MD5_Update(&ctx, context->clientQOP, (CC_LONG)strlen(context->clientQOP)); } CC_MD5_Update(&ctx, ":", 1); CC_MD5_Update(&ctx, a2, (CC_LONG)strlen(a2)); CC_MD5_Final(md, &ctx); free(a2); hex_encode(md, sizeof(md), &str); if (str) strlwr(str); return str; }
/* * Compute a HMAC MD5 sum. * Taken from rfc2104, Appendix. */ static void signature_compute_hmac_md5(const u_int8_t *text, int text_len, unsigned char *key, unsigned int key_len, u_int8_t *digest) { CC_MD5_CTX context; unsigned char k_ipad[65]; /* inner padding - key XORd with ipad */ unsigned char k_opad[65]; /* outer padding - key XORd with opad */ unsigned char tk[16]; int i; /* if key is longer than 64 bytes reset it to key=MD5(key) */ if (key_len > 64) { CC_MD5_CTX tctx; CC_MD5_Init(&tctx); CC_MD5_Update(&tctx, key, key_len); CC_MD5_Final(tk, &tctx); key = tk; key_len = 16; } /* * the HMAC_MD5 transform looks like: * * MD5(K XOR opad, MD5(K XOR ipad, text)) * * where K is an n byte key * ipad is the byte 0x36 repeated 64 times * opad is the byte 0x5c repeated 64 times * and text is the data being protected */ /* start out by storing key in pads */ memset(k_ipad, 0, sizeof k_ipad); memset(k_opad, 0, sizeof k_opad); memcpy(k_ipad, key, key_len); memcpy(k_opad, key, key_len); /* XOR key with ipad and opad values */ for (i=0; i<64; i++) { k_ipad[i] ^= 0x36; k_opad[i] ^= 0x5c; } /* * perform inner MD5 */ CC_MD5_Init(&context); /* init context for 1st pass */ CC_MD5_Update(&context, k_ipad, 64); /* start with inner pad */ CC_MD5_Update(&context, text, text_len); /* then text of datagram */ CC_MD5_Final(digest, &context); /* finish up 1st pass */ /* * perform outer MD5 */ CC_MD5_Init(&context); /* init context for 2nd pass */ CC_MD5_Update(&context, k_opad, 64); /* start with outer pad */ CC_MD5_Update(&context, digest, 16); /* then results of 1st hash */ CC_MD5_Final(digest, &context); /* finish up 2nd pass */ }
int sCCHashUpdateMD5(void *ctx, const unsigned char *in, unsigned long inlen) { CC_MD5_Update(ctx, in, (CC_LONG)inlen); return CRYPT_OK; }
CFStringRef FileMD5HashCreateWithPath(CFStringRef filePath, size_t chunkSizeForReadingData) { CFStringRef result = NULL; CFReadStreamRef readStream = NULL; CFURLRef fileURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (CFStringRef)filePath, kCFURLPOSIXPathStyle, (Boolean) false); if (!fileURL) { goto done; } readStream = CFReadStreamCreateWithFile(kCFAllocatorDefault, (CFURLRef)fileURL); if (!readStream) { goto done; } bool didSucceed = (bool)CFReadStreamOpen(readStream); if (!didSucceed) { goto done; } // Initialize the hash object CC_MD5_CTX hashObject; CC_MD5_Init(&hashObject); // Make sure the chunkSizeForReadindData is valid if (!chunkSizeForReadingData) { chunkSizeForReadingData = FileHashDefaultChunksSizeForReadingData; } // Feed the data to the hash object bool hasMoreData = true; while (hasMoreData) { uint8_t buffer[chunkSizeForReadingData]; CFIndex readBytesCount = CFReadStreamRead(readStream, (UInt8 *)buffer, (CFIndex)sizeof(buffer)); if (readBytesCount == -1) { break; } if (readBytesCount == 0) { hasMoreData = false; continue; } CC_MD5_Update(&hashObject, (const void *)buffer, (CC_LONG)readBytesCount); } // Check if the read operation has succeded didSucceed = !hasMoreData; // Compute the hash digest unsigned char digest[CC_MD5_DIGEST_LENGTH]; CC_MD5_Final(digest, &hashObject); // Abort if the read operation fails if (!didSucceed) { goto done; } // Compute the string result char hash[2 * sizeof(digest) + 1]; for (size_t i = 0; i < sizeof(digest); ++i) { snprintf(hash + (2 * i), 3, "%02x", (int)(digest[i])); } result = CFStringCreateWithCString(kCFAllocatorDefault, (const char *)hash, kCFStringEncodingUTF8); done: if (readStream) { CFReadStreamClose(readStream); CFRelease(readStream); } if (fileURL) { CFRelease(fileURL); } return result; }
CFStringRef GCCreateMD5HashWithPath(CFStringRef filePath) { CFStringRef result = NULL; CFReadStreamRef readStream = NULL; CFURLRef fileURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (CFStringRef)filePath, kCFURLPOSIXPathStyle, (Boolean)false); if (!fileURL) goto done; readStream = CFReadStreamCreateWithFile(kCFAllocatorDefault, (CFURLRef)fileURL); if (!readStream) goto done; bool didSucceed = (bool)CFReadStreamOpen(readStream); if (!didSucceed) goto done; CC_MD5_CTX hashObject; CC_MD5_Init(&hashObject); bool hasMoreData = true; while (hasMoreData) { uint8_t buffer[GCFileChunkSize]; CFIndex readBytesCount = CFReadStreamRead(readStream, (UInt8 *)buffer, (CFIndex)sizeof(buffer)); if (readBytesCount == -1) break; if (readBytesCount == 0) { hasMoreData = false; continue; } CC_MD5_Update(&hashObject, (const void *)buffer, (CC_LONG)readBytesCount); } didSucceed = !hasMoreData; unsigned char digest[CC_MD5_DIGEST_LENGTH]; CC_MD5_Final(digest, &hashObject); if (!didSucceed) goto done; char hash[2 * sizeof(digest) + 1]; for (size_t i = 0; i < sizeof(digest); ++i) { snprintf(hash + (2 * i), 3, "%02x", (int)(digest[i])); } result = CFStringCreateWithCString(kCFAllocatorDefault, (const char *)hash, kCFStringEncodingUTF8); done: if (readStream) { CFReadStreamClose(readStream); CFRelease(readStream); } if (fileURL) { CFRelease(fileURL); } return result; }
void cCCHmacUpdate(CCHmacContext *ctx, const void *d, size_t dl) { /* 3. Append text to the result of step 2 */ /* 4. Compute MD5 of result of step 3 */ CC_MD5_Update(&ctx->md5ctx, d, (CC_LONG)dl); }
void MD5Digest::Update(const void* buffer, size_t length) { CC_MD5_Update(&mContext, buffer, (CC_LONG)length); }
static char * build_A1_hash(int type, const char *username, const char *password, const char *realm, const char *serverNonce, const char *clientNonce, const char *auth_id) { unsigned char md[CC_MD5_DIGEST_LENGTH]; CC_MD5_CTX ctx; char *A1; CC_MD5_Init(&ctx); CC_MD5_Update(&ctx, username, strlen(username)); CC_MD5_Update(&ctx, ":", 1); CC_MD5_Update(&ctx, realm, strlen(realm)); CC_MD5_Update(&ctx, ":", 1); CC_MD5_Update(&ctx, password, strlen(password)); CC_MD5_Final(md, &ctx); if (type != HEIM_DIGEST_TYPE_RFC2069) { CC_MD5_Init(&ctx); CC_MD5_Update(&ctx, md, sizeof(md)); memset(md, 0, sizeof(md)); CC_MD5_Update(&ctx, ":", 1); CC_MD5_Update(&ctx, serverNonce, strlen(serverNonce)); if (clientNonce) { CC_MD5_Update(&ctx, ":", 1); CC_MD5_Update(&ctx, clientNonce, strlen(clientNonce)); } if (auth_id) { CC_MD5_Update(&ctx, ":", 1); CC_MD5_Update(&ctx, auth_id, strlen(auth_id)); } CC_MD5_Final(md, &ctx); } hex_encode(md, sizeof(md), &A1); if (A1) strlwr(A1); return A1; }