900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > SHA224和SHA256哈希算法原理及实现(附源码)

SHA224和SHA256哈希算法原理及实现(附源码)

时间:2019-10-04 02:14:45

相关推荐

SHA224和SHA256哈希算法原理及实现(附源码)

相关文章:

SHA224和SHA256哈希算法原理及实现(附源码)国密SM3哈希算法原理及实现(附源码)SHA1哈希算法原理及实现(附源码)MD5哈希算法原理及实现(附源码)MD4哈希算法原理及实现(附源码)MD2哈希算法原理及实现(附源码)MD2中用于随机置换的S盒是如何生成的?

最近陆续造了一批哈希算法的轮子,包括MD家族(包括MD2/MD4/MD5), SHA1, SHA2家族(SHA224, SHA256, SHA384, SHA512),SHA3家族以及国密SM3算法。

原来打算将每一个算法都详细分析并实现,现在看来,这个工作短时间可能无法完成,所以先将源码发上来。

这部分实现的源码完全参考官方文档的算法描述,连变量名也尽可能和官方文档中的变量保持一致,方便学习。

另外, 代码封装的SHA256和SHA224哈希调用接口参考了openssl官方的接口,完全兼容,无缝对接。会使用这里的接口,就会使用openssl的库函数接口,甚至连代码都不需要修改。

除了实现的源码外,还另外附带了一个测试例子,这个测试例子不仅仅是用于测试哈希算法的实现是否正确,还可以提供了"-f"/"-s"等选项用于对任意文件和字符串进行哈希,因此作为一个工具使用,类似系统内置的md5sum/sha1sum。

SHA224和SHA256实现源码

1. 头文件sha256.c

/** @ file: sha256.h* @ description: header file for sha256.c* @author: Gu Yongqiang* @ blog: /guyongqiangx*/#ifndef __ROCKY_SHA256__H#define __ROCKY_SHA256__H#define ERR_OK 0#define ERR_ERR -1 /* generic error */#define ERR_INV_PARAM -2 /* invalid parameter */#define ERR_TOO_LONG -3 /* too long */#define ERR_STATE_ERR -4 /* state error */typedef unsigned charuint8_t;typedef unsigned shortuint16_t;typedef unsigned int uint32_t;typedef unsigned long long uint64_t;typedef struct sha256_context {/* message total length in bytes */uint64_t total;/* intermedia hash value for each block */struct {uint32_t a;uint32_t b;uint32_t c;uint32_t d;uint32_t e;uint32_t f;uint32_t g;uint32_t h;}hash;/* last block */struct {uint32_t used;/* used bytes */uint8_t buf[64]; /* block data buffer */}last;}SHA256_CTX;/* /docs/man1.1.1/man3/SHA256_Final.html */int SHA224_Init(SHA256_CTX *c);int SHA224_Update(SHA256_CTX *c, const void *data, size_t len);int SHA224_Final(unsigned char *md, SHA256_CTX *c);unsigned char *SHA224(const unsigned char *d, size_t n, unsigned char *md);int SHA256_Init(SHA256_CTX *c);int SHA256_Update(SHA256_CTX *c, const void *data, size_t len);int SHA256_Final(unsigned char *md, SHA256_CTX *c);unsigned char *SHA256(const unsigned char *d, size_t n, unsigned char *md);#endif

2. 代码文件sha256.c

/** @ file: sha256.c* @ description: implementation for the SHA224/SHA256 Secure Hash Algorithm* @author: Gu Yongqiang* @ blog: /guyongqiangx*/#include <stdio.h>#include <string.h>#include "utils.h"#include "sha256.h"//#define DEBUG#ifdef DEBUG#define DBG(...) printf(__VA_ARGS__)#define DUMP_BLOCK_DATA 1#define DUMP_BLOCK_HASH 1#define DUMP_ROUND_DATA 1#else#define DBG(...)#define DUMP_BLOCK_DATA 0#define DUMP_BLOCK_HASH 0#define DUMP_ROUND_DATA 0#endif#define SHA256_BLOCK_SIZE 64 /* 512 bits = 64 Bytes */#define SHA256_LEN_SIZE 8 /* 64 bits = 8 bytes */#define SHA256_LEN_OFFSET (SHA256_BLOCK_SIZE - SHA256_LEN_SIZE)#define SHA256_DIGEST_SIZE32 /* 256 bits = 32 bytes */#define SHA224_DIGEST_SIZE28 /* 224 bits = 28 bytes */#define SHA256_PADDING_PATTERN0x80#define SHA256_ROUND_NUM 64#define HASH_BLOCK_SIZE SHA256_BLOCK_SIZE#define HASH_LEN_SIZESHA256_LEN_SIZE#define HASH_LEN_OFFSET SHA256_LEN_OFFSET#define HASH_DIGEST_SIZE SHA256_DIGEST_SIZE/* use sha256 digest size */#define HASH_PADDING_PATTERN SHA256_PADDING_PATTERN#define HASH_ROUND_NUM SHA256_ROUND_NUM/* SHA256 Constants */static const uint32_t K256[HASH_ROUND_NUM] = {0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2};/* ROTate Right (cirular right shift) */static uint32_t ROTR(uint32_t x, uint8_t shift){return (x >> shift) | (x << (32 - shift));}/* Right SHift */static uint32_t SHR(uint32_t x, uint8_t shift){return (x >> shift);}/* Ch ... choose */static uint32_t Ch(uint32_t x, uint32_t y, uint32_t z){return (x & y) ^ (~x & z) ;}/* Maj ... majority */static uint32_t Maj(uint32_t x, uint32_t y, uint32_t z){return (x & y) ^ (x & z) ^ (y & z);}/* SIGMA0 */static uint32_t SIGMA0(uint32_t x){return ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22);}/* SIGMA1 */static uint32_t SIGMA1(uint32_t x){return ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25);}/* sigma0, different from SIGMA0 */static uint32_t sigma0(uint32_t x){return ROTR(x, 7) ^ ROTR(x, 18) ^ SHR(x, 3);}/* sigma1, different from SIGMA1 */static uint32_t sigma1(uint32_t x){return ROTR(x, 17) ^ ROTR(x, 19) ^ SHR(x, 10);}int SHA256_Init(SHA256_CTX *c){if (NULL == c){return ERR_INV_PARAM;}memset(c, 0, sizeof(SHA256_CTX));/* Initial Value for SHA256 */c->hash.a = 0x6a09e667;c->hash.b = 0xbb67ae85;c->hash.c = 0x3c6ef372;c->hash.d = 0xa54ff53a;c->hash.e = 0x510e527f;c->hash.f = 0x9b05688c;c->hash.g = 0x1f83d9ab;c->hash.h = 0x5be0cd19;return ERR_OK;}static int SHA256_PrepareScheduleWord(const uint32_t *block, uint32_t *W){uint32_t t;if ((NULL == block) || (NULL == W)){return ERR_INV_PARAM;}for (t=0; t<HASH_ROUND_NUM; t++){if (t<=15) /* 0 <= t <= 15 */W[t] = be32toh(block[t]);else /* 16 <= t <= 79 */W[t] = sigma1(W[t-2]) + W[t-7] + sigma0(W[t-15]) + W[t-16];}return ERR_OK;}static int SHA256_ProcessBlock(SHA256_CTX *ctx, const void *block){uint32_t t;uint32_t W[HASH_ROUND_NUM];uint32_t T1, T2;uint32_t a, b, c, d, e, f, g, h;if ((NULL == ctx) || (NULL == block)){return ERR_INV_PARAM;}#if (DUMP_BLOCK_DATA == 1)DBG("---------------------------------------------------------\n");DBG(" BLOCK: %llu\n", ctx->total/HASH_BLOCK_SIZE);DBG(" DATA:\n");print_buffer(block, HASH_BLOCK_SIZE, " ");#endif/* prepare schedule word */SHA256_PrepareScheduleWord(block, W);a = ctx->hash.a;b = ctx->hash.b;c = ctx->hash.c;d = ctx->hash.d;e = ctx->hash.e;f = ctx->hash.f;g = ctx->hash.g;h = ctx->hash.h;#if (DUMP_BLOCK_HASH == 1)DBG("IV: %08x %08x %08x %08x %08x %08x %08x %08x\n",ctx->hash.a, ctx->hash.b, ctx->hash.c, ctx->hash.d, ctx->hash.e, ctx->hash.f, ctx->hash.g, ctx->hash.h);#endiffor (t=0; t<HASH_ROUND_NUM; t++){T1 = h + SIGMA1(e) + Ch(e, f, g) + K256[t] + W[t];T2 = SIGMA0(a) + Maj(a, b, c);h = g;g = f;f = e;e = d + T1;d = c;c = b;b = a;a = T1 + T2;#if (DUMP_ROUND_DATA == 1)DBG("%02d: T1=0x%08x, T2=0x%08x, W=0x%08x, \n"\" a=0x%08x, b=0x%08x, c=0x%08x, d=0x%08x, e=0x%08x, f=0x%08x, g=0x%08x, h=0x%08x\n", \t, T1, T2, W[t], a, b, c, d, e, f, g, h);#endif}ctx->hash.a += a;ctx->hash.b += b;ctx->hash.c += c;ctx->hash.d += d;ctx->hash.e += e;ctx->hash.f += f;ctx->hash.g += g;ctx->hash.h += h;#if (DUMP_BLOCK_HASH == 1)DBG(" HASH: %08x %08x %08x %08x %08x %08x %08x %08x\n",ctx->hash.a, ctx->hash.b, ctx->hash.c, ctx->hash.d, ctx->hash.e, ctx->hash.f, ctx->hash.g, ctx->hash.h);#endifreturn ERR_OK;}int SHA256_Update(SHA256_CTX *c, const void *data, size_t len){uint32_t copy_len = 0;if ((NULL == c) || (NULL == data)){return ERR_INV_PARAM;}/* has used data */if (c->last.used != 0){/* less than 1 block in total, combine data */if (c->last.used + len < HASH_BLOCK_SIZE){memcpy(&c->last.buf[c->last.used], data, len);c->last.used += len;return ERR_OK;}else /* more than 1 block */{/* process the block in context buffer */copy_len = HASH_BLOCK_SIZE - c->last.used;memcpy(&c->last.buf[c->last.used], data, copy_len);SHA256_ProcessBlock(c, &c->last.buf);c->total += HASH_BLOCK_SIZE;data = (uint8_t *)data + copy_len;len -= copy_len;/* reset context buffer */memset(&c->last.buf[0], 0, HASH_BLOCK_SIZE);c->last.used = 0;}}/* less than 1 block, copy to context buffer */if (len < HASH_BLOCK_SIZE){memcpy(&c->last.buf[c->last.used], data, len);c->last.used += len;return ERR_OK;}else{/* process data blocks */while (len >= HASH_BLOCK_SIZE){SHA256_ProcessBlock(c, data);c->total += HASH_BLOCK_SIZE;data = (uint8_t *)data + HASH_BLOCK_SIZE;len -= HASH_BLOCK_SIZE;}/* copy rest data to context buffer */memcpy(&c->last.buf[0], data, len);c->last.used = len;}return ERR_OK;}int SHA256_Final(unsigned char *md, SHA256_CTX *c){uint32_t *temp;//uint64_t *buf;if ((NULL == c) || (NULL == md)){return ERR_INV_PARAM;}/* Last block should be less thant HASH_BLOCK_SIZE - HASH_LEN_SIZE */if (c->last.used >= (HASH_BLOCK_SIZE - HASH_LEN_SIZE)){c->total += c->last.used;/* one more block */c->last.buf[c->last.used] = HASH_PADDING_PATTERN;c->last.used++;memset(&c->last.buf[c->last.used], 0, HASH_BLOCK_SIZE - c->last.used);SHA256_ProcessBlock(c, &c->last.buf);memset(&c->last.buf[0], 0, HASH_BLOCK_SIZE - HASH_LEN_SIZE);c->last.used = 0;/* save length *///buf = (uint64_t *)&(c->last.buf[HASH_LEN_OFFSET]);//*buf = htobe64(c->total << 3);temp = (uint32_t *)&(c->last.buf[HASH_LEN_OFFSET]);temp[0] = htobe32((c->total << 3) >> 32 & 0xFFFFFFFF);temp[1] = htobe32((c->total << 3) & 0xFFFFFFFF);SHA256_ProcessBlock(c, &c->last.buf);}else /* 0 <= last.used < HASH_BLOCK_SIZE - HASH_LEN_SIZE */{c->total += c->last.used;/* one more block */c->last.buf[c->last.used] = HASH_PADDING_PATTERN;c->last.used++;/* padding 0s */memset(&c->last.buf[c->last.used], 0, HASH_BLOCK_SIZE - HASH_LEN_SIZE - c->last.used);/* save length *///buf = (uint64_t *)&(c->last.buf[HASH_LEN_OFFSET]);//*buf = htobe64(c->total << 3);temp = (uint32_t *)&(c->last.buf[HASH_LEN_OFFSET]);temp[0] = htobe32((c->total << 3) >> 32 & 0xFFFFFFFF);temp[1] = htobe32((c->total << 3) & 0xFFFFFFFF);SHA256_ProcessBlock(c, &c->last.buf);}temp = (uint32_t *)md;temp[0] = htobe32(c->hash.a);temp[1] = htobe32(c->hash.b);temp[2] = htobe32(c->hash.c);temp[3] = htobe32(c->hash.d);temp[4] = htobe32(c->hash.e);temp[5] = htobe32(c->hash.f);temp[6] = htobe32(c->hash.g);temp[7] = htobe32(c->hash.h);return ERR_OK;}unsigned char *SHA256(const unsigned char *d, size_t n, unsigned char *md){SHA256_CTX c;if ((NULL == d) || (NULL == md)){return NULL;}SHA256_Init(&c);SHA256_Update(&c, d, n);SHA256_Final(md, &c);return md;}int SHA224_Init(SHA256_CTX *c){if (NULL == c){return ERR_INV_PARAM;}memset(c, 0, sizeof(SHA256_CTX));c->hash.a = 0xc1059ed8;c->hash.b = 0x367cd507;c->hash.c = 0x3070dd17;c->hash.d = 0xf70e5939;c->hash.e = 0xffc00b31;c->hash.f = 0x68581511;c->hash.g = 0x64f98fa7;c->hash.h = 0xbefa4fa4;return ERR_OK;}int SHA224_Update(SHA256_CTX *c, const void *data, size_t len){return SHA256_Update(c, data, len);}int SHA224_Final(unsigned char *md, SHA256_CTX *c){int rc = ERR_OK;unsigned char sha256_md[SHA256_DIGEST_SIZE];memset(&sha256_md, 0, sizeof(sha256_md));rc = SHA256_Final(sha256_md, c);memcpy(md, sha256_md, SHA224_DIGEST_SIZE);return rc;}unsigned char *SHA224(const unsigned char *d, size_t n, unsigned char *md){SHA256_CTX c;if ((NULL == d) || (NULL == md)){return NULL;}SHA224_Init(&c);SHA224_Update(&c, d, n);SHA224_Final(md, &c);return md;}

从上面的实现来看,SHA224和SHA256的主要区别在于:

SHA224_Init函数中,初始化变量不一致SHA224_Final函数中,从基于SHA256得到的哈希中截取前面部分作为SHA224的哈希值

SHA256源码的编译和测试

我直接在Makefile中内置了一个test伪目标,编译时除了编译生成名为sha256的哈希工具外,还会直接调用内置的哈希测试。

编译和运行如下:

$ makegcc -Wall -g -O2 -c utils.c -o utils.ogcc -Wall -g -O2 -c sha256.c -o sha256.ogcc -Wall -g -O2 -c sha256test.c -o sha256test.ogcc -Wall -g -O2 utils.o sha256.o sha256test.o -o sha256Run Test..../sha256 -a sha224 -xInternal hash tests for ./sha256(SHA224):./sha256("")Expect: d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42fResult: d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f./sha256("a")Expect: abd37534c7d9a2efb9465de931cd7055ffdb8879563ae98078d6d6d5Result: abd37534c7d9a2efb9465de931cd7055ffdb8879563ae98078d6d6d5./sha256("abc")Expect: 23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7Result: 23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7./sha256("message digest")Expect: 2cb21c83ae2f004de7e81c3c7019cbcb65b71ab656b22d6d0c39b8ebResult: 2cb21c83ae2f004de7e81c3c7019cbcb65b71ab656b22d6d0c39b8eb./sha256("abcdefghijklmnopqrstuvwxyz")Expect: 45a5f72c39c5cff2522eb3429799e49e5f44b356ef926bcf390dccc2Result: 45a5f72c39c5cff2522eb3429799e49e5f44b356ef926bcf390dccc2./sha256("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")Expect: bff72b4fcb7d75e5632900ac5f90d219e05e97a7bde72e740db393d9Result: bff72b4fcb7d75e5632900ac5f90d219e05e97a7bde72e740db393d9./sha256("12345678901234567890123456789012345678901234567890123456789012345678901234567890")Expect: b50aecbe4e9bb0b57bc5f3ae760a8e01db24f203fb3cdcd13148046eResult: b50aecbe4e9bb0b57bc5f3ae760a8e01db24f203fb3cdcd13148046e./sha256 -a sha256 -xInternal hash tests for ./sha256(SHA256):./sha256("")Expect: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855Result: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855./sha256("a")Expect: ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bbResult: ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb./sha256("abc")Expect: ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015adResult: ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad./sha256("message digest")Expect: f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650Result: f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650./sha256("abcdefghijklmnopqrstuvwxyz")Expect: 71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73Result: 71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73./sha256("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")Expect: db4bfcbd4da0cd85a60c3c37d3fbd8805c77f15fc6b1fdfe614ee0a7c8fdb4c0Result: db4bfcbd4da0cd85a60c3c37d3fbd8805c77f15fc6b1fdfe614ee0a7c8fdb4c0./sha256("12345678901234567890123456789012345678901234567890123456789012345678901234567890")Expect: f371bc4a311f2b009eef952dd83ca80e2b60026c8e935592d0f9c308453c813eResult: f371bc4a311f2b009eef952dd83ca80e2b60026c8e935592d0f9c308453c813e

最新版本的openssl工具已经支持sm3哈希算法,因此可以将sm3工具和openssl执行dgst计算的结果进行比较:

$ sha256 -hUsage:Common options: [-x|-f file|-s string| -a sha224|sha256 | -h]Hash a string:sha256 -a sha224|sha256 -s stringHash a file:sha256 -a sha224|sha256 -f file [-k key]-aSecure hash algorithm: "sha224", "sha256"-xInternal string hash test-hDisplay this message## 计算SHA224哈希## 使用"-f"和"-s"选项分别对文件和字符串计算sha224哈希值$ sha256 -a sha224 -f sha256.osha256(sha256.o) = 3ab679330b579d31c032c29d648d873ee95b03e0be085640e468fba9$ sha256 -a sha224 -s "I Love China!"sha256("I Love China!") = ea5e741f52612d3897304d6a70c146a7fa21b965ed28739f091396e8# 使用开源的openssl工具计算相应的哈希进行对比$ openssl dgst -sha224 sha256.oSHA224(sha256.o)= 3ab679330b579d31c032c29d648d873ee95b03e0be085640e468fba9$ echo -n "I Love China!" | openssl dgst -sha224(stdin)= ea5e741f52612d3897304d6a70c146a7fa21b965ed28739f091396e8## 计算SHA256哈希## 使用"-f"和"-s"选项分别对文件和字符串计算sha256哈希值$ sha256 -a sha256 -f sha256.osha256(sha256.o) = 08b4685632df74d8fc765fa70ccc4ab9763c9e4a6a69b3c5a53f73173122bac5$ sha256 -a sha256 -s "I Love China!"sha256("I Love China!") = 91c906339dbb1f46cfcb2a24dfe5bc445752a84fc04a8474b4260fd8bb679129# 使用开源的openssl工具计算相应的哈希进行对比$ openssl dgst -sha256 sha256.oSHA256(sha256.o)= 08b4685632df74d8fc765fa70ccc4ab9763c9e4a6a69b3c5a53f73173122bac5$ echo -n "I Love China!" | openssl dgst -sha256(stdin)= 91c906339dbb1f46cfcb2a24dfe5bc445752a84fc04a8474b4260fd8bb679129

完整代码

完整的代码文件列表如下:

sha256$ ls -lhtotal 52K-rwxr--r-- 1 rocky rocky 649 Jun 20 15:47 Makefile-rwxrwxr-x 1 rocky rocky 12K Jun 20 17:06 sha256.c-rwxrwxr-x 1 rocky rocky 1.6K Jun 20 17:04 sha256.h-rwxrwxr-x 1 rocky rocky 12K Jun 20 16:49 sha256test.c-rwxr--r-- 1 rocky rocky 8.1K Jun 20 11:04 sha256test-new.c-rwxr--r-- 1 rocky rocky 758 Jun 20 17:11 utils.c-rwxr--r-- 1 rocky rocky 1.8K Jun 20 17:11 utils.h

需要代码请访问:

/guyongqiangx/cryptography/

其它

洛奇工作中常常会遇到自己不熟悉的问题,这些问题可能并不难,但因为不了解,找不到人帮忙而瞎折腾,往往导致浪费几天甚至更久的时间。

所以我组建了几个微信讨论群(记得微信我说加哪个群,如何加微信见后面),欢迎一起讨论:

一个密码编码学讨论组,主要讨论各种加解密,签名校验等算法,请说明加密码学讨论群。一个Android OTA的讨论组,请说明加Android OTA群。一个git和repo的讨论组,请说明加git和repo群。

在工作之余,洛奇尽量写一些对大家有用的东西,如果洛奇的这篇文章让您有所收获,解决了您一直以来未能解决的问题,不妨赞赏一下洛奇,这也是对洛奇付出的最大鼓励。扫下面的二维码赞赏洛奇,金额随意:

洛奇自己维护了一个公众号“洛奇看世界”,一个很佛系的公众号,不定期瞎逼逼。公号也提供个人联系方式,一些资源,说不定会有意外的收获,详细内容见公号提示。扫下方二维码关注公众号:

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。