想起了奶奶

4、5月份是播种的季节,然而我这两天为买秧苗犯了愁,虽然大概10公里以外有一个农贸大集,但是沿途特别堵车,到了地方也很难找到停车位,愁啊。


(图源 :pixabay)

上午的时候,去附近的露天市场看了一下,发现有卖菜的、卖水果的、卖鱼卖肉的、甚至还发现卖花的,就是没有发现卖秧苗的,看来哪天免不了跑一趟了。

提到了秧苗,我就想起了我的奶奶,记得当年学过朱德的一篇文章《回忆我的母亲》其中有一句话:母亲是个好劳动,尽管好多年过去,我依然对这个句式有些迷蝴,但是当年我还是一下理解了句子的意思。

因为我的奶奶也是个好劳动,其中的一项就是每逢春天,奶奶都要在院子里弄一个小小的塑料罩起来的苗圃,然后种上一些茄子、西红柿等的种子,然后等它们长到大约10厘米的样子,就会拿到市场上去卖。

现在那时候的小秧苗并不值几个钱,一小簇小苗才卖几毛钱,一大堆秧苗也就卖十几块钱,不过在那时候这可是一笔巨款,奶奶基本上都会把这笔巨款给我们买好吃的或者买一些文具。

秧苗除了出售,自己也要在院子里栽种,所以每年我们都能吃到又大又红的西红柿,还有清香中带一丝丝甜味的茄子,至于黄瓜、青椒、豆角、芹菜、白菜等等,这些从来就没有缺席过。

奶奶每年还会再围墙边上种一些叫做甜干的东西,外观看起来像是高粱,但是秸秆嚼起来和甘蔗差不多水分特别足也特别的甜,是我们小时侯最喜欢的“零食”之一。

到了甜干收获的季节,奶奶会带着我们把吃不了的甜干弄到集市上去卖,又是一笔小小的收获。

甜干除了秸秆能当做食物,也会结出像高粱一样的穗子,奶奶把这些穗子收集起来,把上边的种子敲掉,然后把穗子晒干后,用自己的工具以及很结实的麻线捆一下,就变成了刷锅用的刷子或者扫地用的扫帚,再市场上非常受欢迎的。

在奶奶的眼中,每样作物都很很多很多的价值,而且奶奶也能把它们充分利用,比如说葫芦,再葫芦还很青的时候是可以当青菜炒着吃的,而葫芦长成之后,晒干用小锯锯成两半,无论是用来舀水还是舀米都是非常得手的工具。

小时侯跟奶奶学了很多农活和技巧,然而现在那些技巧大多无了用武之地。就算是农活,现在虽然还会干,但是却总感觉干不动了。

下午的时候在院子里拔了一些草并且种了一点白菜,就累得腰酸背痛的,而那时奶奶70多岁,干农活还是做家务,却从来没有喊累的时候,这就是我的好劳动的奶奶啊。


(图源 :pixabay)

有时候不禁去想,如果有天国或者有来世,奶奶是不是还是喜欢弄她的小苗圃呢?还会种一些甜干什么的作物吗?应该会是吧。


This page is synchronized from the post: ‘想起了奶奶’

每天进步一点点:字节序的问题 (little-endian/big-endian)

在Memo使用AES加密的过程中,除了使用到shared_secret外,还涉及到两个数值:分别是nonce以及check


(图源 :pixabay)

nonce & check

其中nonce是一个64位的随机数(8字节),在BM的一篇文章中曾有提及:

The purpose of the ‘nonce’ field is to generate a unique encryption key for every transfer between two accounts. I it should be a random number.

check则是密钥的32位(4字节)摘要,保证字节串中内容没被破坏掉(其实我觉得并没有啥用,因为它只对加密密钥检查,并没有检查加密后的信息)

这两个数值的具体作用我们就简单说到这里,之所以提到这两个数值,是因为这两个数值要和加密后的文本一起用base58编码打包进memo。

因为打包用的是字符串串接,所以我自作聪明地写了类似如下代码:

str_nonce = '%016x' % nonce
str_check = '%08x' % check

目的是把这两个转成字符串,然后串接到Memo包里。然而加密Memo是生成了,解密时却出现:

assert check == checksum, “Checksum failure”
AssertionError: Checksum failure

这充分说明我传入的数值不对嘛,可是问题出在哪里呢?我百思不得其解。

字节序 / Byte Order

其实稍微分析一下,这事就不难想明白。我不过是将数据打包进memo,然后再拆包出来,拆包的部分代码如下:

nonce = str(struct.unpack_from("<Q", unhexlify(raw[:16]))[0])
raw = raw[16:]
check = struct.unpack_from("<I", unhexlify(raw[:8]))[0]

注意到什么了没有?没错,就是字节序(Byte Order)的问题!什么叫做字节序呢?简单来讲就是:

大于一个字节类型的数据在内存中的存放或者传递时顺序。

而Python struct 中字节序的标识如下:

image.png

由此不难看出,我们解包时是按little-endian(<)解包noncecheck的,亦即:

低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。

再对比一下如下代码:

a = 0x0123456789abcdef
str_1 = '%016x' % a
str_2 = hexlify(struct.pack("<Q", a)).decode()
str_3 = hexlify(struct.pack(">Q", a)).decode()
print(str_1, str_2, str_3)

输出为:0123456789abcdef efcdab8967452301 0123456789abcdef

由此可见,直接用%016x生成的字符串是把原数据按Big endian放置的!

解决方法

知道了引起错误的原因是因为字节序问题,那么解决起来就好办多了,使用如下代码生成字符串即可:

str_nonce = hexlify(struct.pack("<Q", int(nonce))).decode()
str_check = hexlify(struct.pack("<I", int(check))).decode()

你可能会问,既然是字节序问题,那么我们解包的程序也按着相同的字节序处理不也一样嘛?道理是这个道理,不过问题是无论打包还是解包都单单自己要用,你可能要给别人发送memo,也可能接收别人的memo,所以必须按着相同的规范和标准来哦

相关链接


This page is synchronized from the post: ‘每天进步一点点:字节序的问题 (little-endian/big-endian)’

家花和野花以及青杏

image.png

院子里的鸢尾花开正浓,紫色的、黄色的,争奇斗艳,仿佛都想把对方比下去,然而却又不相伯仲。

有意思的是,往年鸢尾花,黄色开完紫色才开的,貌似花期不在同一时间,而今年或是因为雨水充足的缘故,都一齐冒了出来呢。

image.png

image.png

除了鸢尾,我之前栽种的重牛也在一直地开啊开,一点不甘示弱,不过我总觉得重牛完全是为了开花而开花,太过于辛苦了。

image.png

院子外边我移栽了一些鸢尾,终于有了效果,用过路阿姨说的话:幸苦耕耘总算有了收获。

不过外边的鸢尾也是双色混杂的,可是目前只看到黄色的花开,这真有意思。

image.png

相对于家花的富贵艳丽,野花清香淡雅,也别有一番风味呢。这组马莲花其实颜色要更紫一些,然而手机拍出来颜色显得很淡,没实际的好看呢。

image.png

下边的小花,物业的管家小姑娘说这是二月兰,然而我用软件识别出来的叫诸葛菜,据说是可以吃的哦。

image.png

image.png

蒲公英成熟时的样子大家都清楚,但是其实还是花朵的时候也很漂亮呢。

以前查过,其实蒲公英属于菊科蒲公英属,所以其实蒲公英的花朵说起来也算菊花呢。

image.png

另外院外的杏树杏花已落,结出了青涩的果实

image.png

让我不禁又想吟咏:

花褪残红青杏小。
燕子飞时,绿水人家绕。
枝上柳绵吹又少,天涯何处无芳草。

只是这残红都被我扒拉掉了,这杏也有些大,哈哈。

有时候拿手机随意拍拍,记录一下生活,也挺好的。


This page is synchronized from the post: ‘家花和野花以及青杏’

每天进步一点点:Memo中的加密手段 & AES加密

通过之前的文章,我们已经知晓了Memo的大致工作原理,那就是:

发送者利用发送方的私钥和接收方的公钥生成共享密码,而接收者用接收方的私钥和发送方的公钥同样可以获取共享密码。


(图源 :pixabay)

当然了,共享密码生成还是需要经过一系列的处理,并且发送/接收方要按相同的流程和规范生成,这样才能正确地加密/解密。

那么问题来来了,明文有了,密码有了,我们用什么手段加密明文呢? 因为发送方和接收方用相同的密码加解密,所以要用对称加密算法,所谓对称加密,百度百科上这么介绍的:

采用单钥密码系统的加密方法,同一个密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称为单密钥加密。

而HIVE/STEEM的memo加密使用的是对称加密中最为常见的一种算法:AES (Advanced Encryption Standard)即高级加密标准,听听这名字,就不同凡响,哈哈。

安装pycrypto库

Python中使用AES的一种方法是使用pycrypto库,然后使用如下语句引入AES

from Crypto.Cipher import AES

然而因为我用的是一台新机器,所以出现了如下错误提示:

ModuleNotFoundError: No module named ‘Crypto’

使用如下指令尝试安装pycrypto

pip install pycrypto

结果又出现如下错误:

configure: error: in `/tmp/pip-install-7_25hg67/pycrypto’:
configure: error: no acceptable C compiler found in $PATH

缺啥装啥:

sudo apt install gcc

又出错:

src/MD2.c:31:10: fatal error: Python.h: No such file or directory

#include “Python.h”

还是缺啥装啥:

sudo apt install python3-dev

尽管看起来敲个指令很容易,可是家里的网速,网速真的伤不起

image.png

总算下载完了,再次执行:

pip install pycrypto

安装成功,耶:

image.png

AES的使用

接下来是如何使用AES加密的问题,我把我珍藏多年的《密码编码学与网络安全——原理与实践》找了出来,然后发现根本看不懂。

steem-python中的初始化AES的核心代码如下:

image.png

但是AES.MODE_CBCiv都是些什么鬼?查了半天,终于找到了:

  • CBC(Cipher Block Chaining,加密块链)模式
  • IV: 初始化向量

网上找了一段示例代码

一般都是加密时用到iv,但是把iv以明文编码的形式打包到结果中,然后解密时先取出iv再参与计算:

image.png

而HIVE/STEEM的memo加密则比较先进,iv和key都是由shared_secret、nonce计算得出,同样可以在接收端计算得出,无需明文传递。

所以看起来,还是HIVE/STEEM中对Memo加密处理的方法更先进一些呢。

好了,虽然更深入的理论我是研究不明白了,但是基本应该可以上手做了,撸起袖子加油干!

相关链接


This page is synchronized from the post: ‘每天进步一点点:Memo中的加密手段 & AES加密’

光头

说到光头,理过几次光头,不过不仅冬天冷冻脑袋不说,夏天光头也没有想想中的那么舒服,一旦热的出汗,没有头发帮忙挥发,黏糊糊的贴在头皮上更难受。


(图源 :pixabay)

况且前端时间我又办了理发卡,不去发廊理发就觉得卡白办了一样,所以一段时间内都是去发廊,嗯,顺便弄个造型,看起来舒服那么一丢丢。

不过自从12月份疫情开始,我就没再去过发廊了,怕有风险是一方面,另一方面戴口罩理发什么的,想想就会觉得不舒服,于是我都是自己在家用理发器解决。

不过为了一点形象,总把理发器的卡尺设置为6毫米,这样剪完之后,不至于太难看。当初有一次理了光头在小区里闲逛,被楼下大姨逮到一通批评:一看就像局子刚放出来的,以后别剪光头了。

疫情之后倒是理过一次光头,但是那段时间特冷,冻得自己贼拉难受不说,出去别人也全是异样的眼光,那眼神仿佛在说:这么冷的天,弄个光头,怕是个傻子吧?😳 然后又开始我的卡尺理发之旅。

不过用卡尺剪并且留那么几毫米,特麻烦不说,总感觉不够透彻,不够过瘾。想着当年楼下的大姨已经不知道搬哪里去了,我也从原来的那栋楼搬离,想必再理光头,应该没人批评我了。况且这天气也变暖和了,想必再理个光头,别人的眼光也不会那么异样了。

说干就干,理发器打开,不上卡尺,三下五除二就变成了光头,想后悔也没有机会了,不过看着光头的自己,竟然感觉比长头发还舒服一些,哈哈,是不是有王婆卖瓜的嫌疑?

想起来好多好多年前,有段时间无意中玩起QQ的漂流瓶,有一个人聊了几次聊得挺好,她问我长啥样,我回她说,我是一个光头老宅男,然后被她果断地拉黑了。

其实那时候我不老也不宅头发还很多也很长,被那个朋友拉黑我觉得有点委屈,也有点后悔开玩笑骗她。另外,虽然不爽她以貌取人,不过换个角度想,如果她又胖又宅又没头发,那画面估计我也很难忍受。

不过当年对自己的形容,现在基本都变成现实了,又胖、又老、又宅、又光头,哈哈哈哈。


(图源 :pixabay)

又想起梁咏琪《短发》:

我已剪短我的发
剪断了牵挂
剪一地不被爱的分岔

每次剪头都情不自禁的想起这首歌,有时候想想,我剪掉的是啥呢?是青春?还是过往?或许都不是,就是头发而已。想起这首歌,只不过是当年太喜欢这首歌罢了。


This page is synchronized from the post: ‘光头’

每天进步一点点:继续探索memo加密 & 椭圆曲线

在之前的文章中,我们学习并测试了Memo加密的原理,一个关键性结论就是:

Pub(Alice) * Priv(Bob) = Pub(Bob) * Priv(Alice)


(图源 :pixabay)

而这个乘积就是被当作Memo加密的密钥来用的,一般代码中称其为:shared_secret

错误的探索

不过看steem-python代码中,只用了这个乘积的x部分。这让我突发奇想,既然只用到乘积中的x部分,那我是否可以只用x部分参与计算呢?

要知道我们使用的公钥都是压缩过的公钥(亦即只取公钥中的x部):

image.png

而运算时,一般都是要先恢复成压缩前的公钥(通过x部算出y部,这极大增加了运算量,如果可以直接用,岂不美哉?

然而事实给了我无情的打击,以下测试代码说明了一且:

pa = ecdsa.SigningKey.from_string(sa, curve=ecdsa.SECP256k1).verifying_key.pubkey.point
print((pa*100).x() == pa.x()*100)

以上代码输出为:False

解读一下,就是用椭圆曲线上的点100再取x部,和椭圆曲线上的点取x部再乘以100并不相同*

继续探索

想想也是,如果椭圆曲线的乘法就是把x, y乘以对应的数值,那还是椭圆曲线吗?虽然之前探索的方向错误了,但是也增长了见识,不算太亏。

有朋友问我,Pub(Alice) * Priv(Bob) = Pub(Bob) * Priv(Alice)这个结论应该没错,但是有推导吗?

这下难住了我,不过我想想之前学习公钥时学到的内容,貌似得出上述结论并不是很难。

还是要回到椭圆曲线上来:

image.png

我们知道,私钥(k)到公钥(K)计算公式:

K=k∗G

其中小k是我们的私钥,G是生成点,K是结果亦即公钥是曲线上的另外一个点。那么假设两组公私钥(K1, k1)以及(K2, k2)分别是Alice以及Bob的公私钥对,那么我们就有如下关系:

K1=k1∗G
K2=k2∗G

那么Alice的私钥乘以Bob的公钥就是:

`=k1K2`*
`=k1(k2∗G)`*
`=k2(k1∗G)`*
`=k2K1`*

所以Pub(Alice) * Priv(Bob) = Pub(Bob) * Priv(Alice)是可以证明的。

公钥的点

我们的公钥都是STMxxx的形式,要怎么还原成椭圆曲线上的点呢?

答案是可以用如下代码实现:

memo_key = 'STM5jy6R2yw6ZdQoUZZxcVCZ2R7TNpbrXzt4BGpAgnRXYrzwjsVvY'
memo_raw= gphBase58CheckDecode(memo_key[3:])
V = ecdsa.VerifyingKey.from_string(unhexlify(memo_raw), curve=ecdsa.SECP256k1)
P = V.pubkey.point

压缩公钥还原的过程是在ecdsa.VerifyingKey.from_string中完成的,其实我们可以直接调用静态方法ecdsa.VerifyingKey._from_compressed那样会直接返回一个点,而不是一个VerifyingKey类实例了。

总之,无论如何我还是老老实实地让他们去做乘法吧,不再妄想把x部先拆出来再做计算了,太囧了。

相关链接


This page is synchronized from the post: ‘每天进步一点点:继续探索memo加密 & 椭圆曲线’

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×