部分重写密码软件

不知道是左脚站在右脚上, 还是右脚站在左脚上, 我们只知道他站在自己的脚趾上.

自上次初学 Python 写一个自己用的密码管理软件 pgman 已经有一年半了, 存在 的问题有:

  • 交互程序, 不方便其它程序使用.

    比如, 有一个脚本, 初始化所有加密分区. 需要在问密码时, 切到这个程序, 调出密码到剪贴板, 再切过来, 粘贴密码.

  • 密码明文存在内存里, 没有锁住, 容易被交换到磁盘, 造成泄漏.

    这个问题, 其实在目前的是没法严格解决的. 不说密码管理软件, 单使用密码 中, 流程太长, 就如浏览器等, 未必在释放内存前清空密码. 但, 在密码管理 软件中, 少一层不必要的泄漏, 总好一层.

  • 访问安全问题.

    人走时, 未必记得退出此程序, 为方便, 也总是常开着. 虽然设有密码遗忘时 间. 但在别人可访问电脑时, 用 GDB 或 其它内存分析工具可完全获得所有密 码.

五一短假, 本来想用还没学会的 ErLang 练手写一个, 但 ErLang 上手不易, 以 上的问题还是无从下牙. 最后, 还是用 C 写了个.

设计上是, 使用会话加密. 会话私钥不加密地放在 U 盘里, 会话公钥放机器上. 有一个服务程序, 加载原来 pgman 的文件, 这个需要询问永久存储的私钥密码, 用会话公钥加密敏感信息, 保留结构, 存在内存里. 另有一个客户端(现在主要功 能是 BASH 脚本写的), 自动检查并挂载 U 盘, 查询信息, 解密. 服务端返回的 信息, 除状态码外, 都是加密的. RSA 块加密有一个好处是加密信息可以自然串 联起来, 而不必解密.

目前只实现读, 还没有实现写. 写需要在服务端做解密, 写用得不多, 移时再 说.

关于 RSA 加密, 测试显示, OpenSSL 的实现, 4096 位的密码操作时间是 512 位 的 120 倍, 与我所理解的 8 log(8) = 24 倍差太多了, 可能的原因是加密 的主要是短字符串, 4096 位浪费的较多.

下次等熟悉后, 考虑买一个 USB-KEY 吧, 不这样山寨了.

一湖春景

宅, 有外出玩的意向, 无咎.

三月即至, 和打油诗, 以忆未名湖春景.

垂柳拂枝怜桃花, 游人临池怜塔影. 最恨人间仲春景, 无风无雨也无晴.



2 * 2 * 3 * 277 * 6047

SWIG 磨人

每次用新工具, 我都要觉得这东西不够灵活.

SWIG 自1995 年开始, 至今也算稳定了. 只是稳定到一个不够 versatile 的状态.

SWIG的Typemap很方便, 只可惜, 找不到将多参数一起解析, 或者解析时引用前面参数的方法. 文档里倒是明确提到 numinputs 须为0或1, 像是不支持这个.

文档里找不到, 邮件列表里没人解答, 虽然还没到最后一步看实现, 估计我也放弃了. 有新资源申请依赖参数时, 手写Python扩展也比这个框架下写接口来得快.

``SWIG垃圾, 支持多语言的自然做不好.''

``框架!''

虽然, 倒是从 numpy.i 里找出一些代码, 包装数组挺方便的.

纯属测试: 中共匪然海外的都与敏感词相关么.

几种简单的排序算法比较次数比较

几种简单的排序算法比较次数比较

1 说明

几种排序算法, 有一些是简单实现, 另有 glibc 的 qsort 和 GNU C++ 库的 std::sort. 下面的时间是排序 long 型数组的时间. 其实, 是一个 C 里 Sequence, 但此 Sequence 占用的时间和时间的误差相近, 在 10% 左右.

比较程序使用 random 生成数组, 在数组比较小时, 排序并打乱数组多次, 取时 间平均值, 最后几个, 因时间比较大, 只比较一次. 所以, 在所有的时间里, 有 打乱数组的时间开销, 大致为 O(size * time(random)). 本文只关心相对比较明 显的差异, 故不细分这些.

各排序算法如其名. 其中 quicksort_nr 是快速排序的非递归版, cqsort 是调 C 标准库函数 qsort. std::sort(iterator) 中的 get/set 不能区分, 统一计在 get 下.

C 的 qsort 由于必须有函数指针, 比较是相对较大开销, 所以在数组较小, 且内 存足够时采用需要额外内存但比较次数最少的归并排序. C++ 采用的是 quicksort 和 heapsort 的杂交 introsort, 比较次数相对归并排序多一点, 但 对整数而言, 比较仅是减法, 受限于内存访问的时间, 估可以次要考虑.

2 sort comparisons table


Algo.Sizetime# comparisons/size# gets/size# sets/size
introsort2^52.29932e-065.274929.386234.00063
heapsort2^52.70095e-065.1821813.22918.45347
mergesort2^52.37965e-063.796959.296956
quicksort2^52.19414e-065.560798.3592.83799
quicksort_nr2^52.15747e-065.369258.235582.85841
cqsort2^52.98512e-063.79623NANA
combsort2^52.41381e-068.8018417.61233.36478
std::sort(iterator)324.68653e-065.9521813.9444NA
std::sort321.65281e-065.94951NANA
introsort2^64.42774e-066.5157111.08174.47673
heapsort2^65.82915e-066.2620715.3059.46844
mergesort2^64.97256e-064.7661510.26626
quicksort2^64.87501e-066.7783410.05663.35011
quicksort_nr2^64.76849e-066.602369.942053.3574
cqsort2^66.40913e-064.76535NANA
combsort2^65.18118e-0611.609423.22344.28761
std::sort(iterator)643.99712e-067.1153315.4967NA
std::sort643.62354e-067.12488NANA
introsort2^71.00313e-057.7648812.82934.99319
heapsort2^71.28292e-057.3085817.346110.4755
mergesort2^71.94854e-055.7494213.24948
quicksort2^71.06718e-057.9855111.7373.83951
quicksort_nr2^71.05284e-057.8048811.6143.8471
cqsort2^71.41386e-055.74657NANA
combsort2^71.22646e-0514.512629.02745.30448
std::sort(iterator)1288.65036e-068.3236917.1383NA
std::sort1287.87806e-068.31551NANA
introsort2^82.16346e-058.9674514.52865.49551
heapsort2^82.79746e-058.3332119.368211.4748
mergesort2^82.38339e-056.7419714.2428
quicksort2^82.26148e-059.2145413.43754.31297
quicksort_nr2^82.29063e-059.0021713.29554.34143
cqsort2^83.07402e-056.74541NANA
combsort2^82.71602e-0517.38334.76726.34737
std::sort(iterator)2561.99121e-059.5476518.8368NA
std::sort2561.73477e-059.57421NANA
introsort2^94.58946e-0510.155616.22476.01352
heapsort2^96.01988e-059.3549621.392212.4821
mergesort2^95.17732e-057.7391117.23911NA
quicksort2^94.92418e-0510.361815.06154.8
quicksort_nr2^94.81522e-0510.183314.95124.81563
cqsort2^96.54729e-057.74022NANA
combsort2^95.884e-0520.352140.70477.38508
std::sort(iterator)5124.32935e-0510.795420.6048NA
std::sort5123.68822e-0510.7974NANA
introsort2^100.00010043811.358217.93566.51449
heapsort2^100.00012830510.36123.397413.4805
mergesort2^100.0001068048.7383318.238310
quicksort2^100.00010464911.575716.74615.26775
quicksort_nr2^100.00010298411.430216.65645.27142
cqsort2^100.0001853988.73907NANA
combsort2^100.00014446923.950847.90198.49806
std::sort(iterator)10248.9258e-0511.954622.1866NA
std::sort10247.89147e-0512.0618NANA
introsort2^110.00023065512.532219.69557.09028
heapsort2^110.00028826711.366225.402514.4817
mergesort2^110.0002396899.736421.236412
quicksort2^110.0002410312.732318.37895.75391
quicksort_nr2^110.00021754612.538318.26075.76871
cqsort2^110.0003054819.737NANA
combsort2^110.00028974927.091954.1849.64027
std::sort(iterator)20480.00019089113.173523.8782NA
std::sort20480.0001666413.1915NANA
introsort2^120.00046250213.841621.60967.6772
heapsort2^120.00057006612.3727.40515.4825
mergesort2^120.00051040910.736522.236512
quicksort2^120.00046962513.945920.08336.24193
quicksort_nr2^120.0004639113.70619.90276.24634
cqsort2^120.00064265710.7388NANA
combsort2^120.00063899930.262260.524510.6047
std::sort(iterator)40960.00040534114.465525.6454NA
std::sort40960.00035872314.6613NANA
introsort2^130.00094431614.980323.25488.18111
heapsort2^130.001234513.372829.409616.483
mergesort2^130.0011129411.736625.236614
quicksort2^130.0010070715.123421.73036.71223
quicksort_nr2^130.00099806514.859921.5356.73174
cqsort2^130.0013335511.7354NANA
combsort2^130.0014358233.842267.684511.7082
std::sort(iterator)81920.00086368615.767427.4121NA
std::sort81920.0007462515.6189NANA
introsort2^140.0021051216.12624.96778.75029
heapsort2^140.0026205214.369431.403517.4816
mergesort2^140.0023129912.738226.238214
quicksort2^140.0024223616.135823.23437.2082
quicksort_nr2^140.0023941416.129923.26887.187
cqsort2^140.0029565112.7369NANA
combsort2^140.0031642637.091274.182512.864
std::sort(iterator)163840.0018147517.111929.2536NA
std::sort163840.0016003817.1486NANA
introsort2^150.0050492317.369626.83739.35675
heapsort2^150.0056817515.364833.398918.474
mergesort2^150.0054972813.733129.233116
quicksort2^150.0046777717.763325.28967.6303
quicksort_nr2^150.0050014917.266224.89327.67592
cqsort2^150.0065320113.7351NANA
combsort2^150.007007340.216780.433413.9531
std::sort(iterator)327680.0038477818.333830.9398NA
std::sort327680.0033502617.9507NANA
introsort2^160.0096925518.517228.58199.94678
heapsort2^160.011969116.361935.397519.4703
mergesort2^160.010597514.732930.232916
quicksort2^160.0094070418.323526.40238.18502
quicksort_nr2^160.0095629718.106626.24758.1935
cqsort2^160.012993514.7351NANA
combsort2^160.014304543.214286.428415.0652
std::sort(iterator)655360.008198519.551932.6143NA
std::sort655360.007059119.4078NANA
introsort2^170.019830919.252129.675810.3184
heapsort2^170.02646817.355437.389520.462
mergesort2^170.024387815.734633.234618
quicksort2^170.02496719.918428.44278.62446
quicksort_nr2^170.020683119.672828.28098.6527
cqsort2^170.02870815.7364NANA
combsort2^170.03145146.213292.426316.2224
std::sort(iterator)1310720.01720620.735934.2805NA
std::sort1310720.014752920.7001NANA
introsort2^180.042837120.776331.991711.08
heapsort2^180.06022518.35439.387821.461
mergesort2^180.05420416.735434.235418
quicksort2^180.044906120.932329.95069.11972
quicksort_nr2^180.044407120.791129.85849.11888
cqsort2^180.061237116.7355NANA
combsort2^180.070429149.212798.425317.2837
std::sort(iterator)2621440.036987121.805635.8416NA
std::sort2621440.033554121.7911NANA
introsort2^190.1053122.116633.975611.7039
heapsort2^190.16923119.35441.387622.4603
mergesort2^190.12893617.736237.236220
quicksort2^190.10564522.126731.60429.58018
quicksort_nr2^190.10500522.23431.77779.59514
cqsort2^190.15018217.7361NANA
combsort2^190.18412852.2115104.42318.3483
std::sort(iterator)5242880.094221123.330637.815NA
std::sort5242880.085955124.1802NANA
introsort2^200.24776423.447536.075412.4453
heapsort2^200.44266320.354543.388723.4609
mergesort2^200.29055618.73638.23620
quicksort2^200.25062623.241233.207410.0712
quicksort_nr2^200.25393123.307133.318110.0623
cqsort2^200.34422118.736NANA
combsort2^200.4308656.2095112.41919.3904
std::sort(iterator)10485760.23045923.955438.9847NA
std::sort10485760.2057224.3193NANA
introsort2^210.55471224.174837.159812.82
heapsort2^211.0754921.354445.388924.4613
mergesort2^210.65845219.736141.236122
quicksort2^210.56490924.339434.798310.5612
quicksort_nr2^210.55677324.050934.580810.582
cqsort2^210.75741919.7364NANA
combsort2^210.97694859.2084118.41720.509
std::sort(iterator)20971520.50442225.502140.9916NA
std::sort20971520.47248925.5631NANA
introsort2^221.2034625.508839.172713.4783
heapsort2^222.4821322.354247.388525.4608
mergesort2^221.3851220.735742.235722
quicksort2^221.208825.605936.527511.0243
quicksort_nr2^221.1937125.868336.81310.995
cqsort2^221.563720.7359NANA
combsort2^222.0283262.2124124.42521.6898
std::sort(iterator)41943041.0736826.534242.514NA
std::sort41943040.97738226.8222NANA
introsort2^232.4810726.994841.425114.2166
heapsort2^235.7018423.353949.388126.4607
mergesort2^232.9349221.735745.235724
quicksort2^232.5393726.489637.922611.5348
quicksort_nr2^232.5229828.030939.391311.4106
cqsort2^233.2848121.7354NANA
combsort2^234.399466.2117132.42322.7599
std::sort(iterator)83886082.2389927.888244.3053NA
std::sort83886082.0583327.8466NANA
introsort2^245.2841128.792744.279615.2239
heapsort2^2412.959524.354151.388327.4607
mergesort2^245.9699422.735746.235724
quicksort2^245.1942527.937639.818211.9814
quicksort_nr2^245.2373527.937639.857311.9697
cqsort2^246.7785722.7356NANA
combsort2^248.8429169.2087138.41723.8582
std::sort(iterator)167772164.7065928.79545.7217NA
std::sort167772164.2997529.2741NANA

對接口編程, 不要對結構編程 ---- Python 經驗對 C 編程的考慮

通常, 如果幾行SHELL可以搞定, 不會用 Python. 幾行 Python 可以搞定的, 也就懶得用 C 了.

Python 最好用的是語言級的 list 和 dict 支持. 還有如 generator 可以帶來另外的編程範式, C 裏類似的效果, 常伴隨理解困難和BUG. 另外一個是Python的文件模塊化, 對象抽象和各種類似于Decorator的東西, 不僅是編程方便, 也提供了編碼工程上必須的分離. lambda 表達式也是 C 裏尚沒有的.

前時, 泛讀了 C Interfaces and Implementations (是的, 窮人的電子版), 突然對以前在哪看過的一句話非常贊同起來: 編程, 對外暴露接口, 不要暴露數據結構. 如此相反的觀點, 任何抽象都太重, 曾經左右我.

如果能接受腳本語言, 自然能接受C語言裏抽象的接口, 而隱藏數據結構. Python 的方便其實也自此而來.

Python 相比 C 的問題主要是運行慢, C 相比 Python 的問題主要是編碼慢, 易錯, 調試難, 缺泛強大的標准庫.

如果有熟悉的 LIST, Hash-Table, AVL, Sequence 的實現, 熟悉到可以像 Python 的 list, dict 那樣隨手拈來, 用 C 編碼也是很快的. 這些數據結構的通用實現, 自然要求隱藏數據結構. 這樣, 只要接口不變, 你可以方便的換一個實現, 只是需要重新編譯, 或者, 再加一層, 用來轉發到具體的實現, 像在很多 C++ 代碼裏見到的 FooInterface, FooImpl.

喜歡 Generator 的人不好應付一些. C 裏有一些 coroutine 的庫, 但在各堆棧跳來跳去是此編程方式的BUG發源地. 用事件狀態機加多線程是C的經典且易理解的編程方式, 我放棄協程這一條, 就像 Lambda 一樣.

程序, 大部分處理數據, 部分生成數據(處理需要理解, 比生成困難), 小部分是生成不可預測的數據, 也就是 BUG 隱藏地和隨機數的生成地.

在所有的調試方法裏, 某些人偏好 printf 和 assert. 對一個函數的前件和後件多作一些 assert, 很多BUG可以不寫測試也能發現. 程序正確性証明的一個方法就是証明不變量, 對不變量作 assert, 就會c對程序的正確性自信得多.

半年用 Python 的經驗是 Python 更容易寫錯誤的代碼, 多半是運行時才會發現. 只是Python會打印一個錯誤堆棧和出錯位置, C 裏同樣的功能可以設置 core 文件大小限制來得到.

昏黄的颜色

颜色能引起回忆, 比我清晰记得的更多. 那一瞬, 仿佛又回到那时, 那时, 你会与我挣抢昏黄煤油灯的光.

C 的一种易调试的宏用法, 以做代码模板

1 C 的一种易调试的宏用法, 以做代码模板

用 C 比较多时, utils 库其实是不缺的, 但要熟悉起来, 编码才会高效. 前一阵子尝试写哈稀表, 承蒙标准云张教主找来一个哈稀表的评测, 里面有些简易实现 -— 是的, 我只看简单的东西. 看完之后, 我站在别人的脚趾头上再实现了几个.

哈稀表实现有开放寻址和链表, 链表实现容易, 但时间和空间开销都较开放寻址 浪费. 做成高效通用的哈稀表, 有时, 多余函数调用的开销也是关键的, 宏和 inline 函数便在此处出现.

inline 函数很好, 不好的地方是类型检查上不绕开不方便, 绕开不安全. 但, 要 函数指针的形式, 便 inline 不得了. 最后一个考虑是宏了.

``该死的宏.''

宏不方便调试. 在写的时候要将几百行串成一行, 编译运行, 可能只告诉你宏调 用那一行有错, 这时要去那几百行慢慢找.

为此, 我看过 CPP 的文档, 失望而归. 然后, 在考虑写软件包自动编译安装脚本时, 发现 M4.

在 M4 之前, 我是想用 sed 来做这事的, 最后还是决定尝试一下不熟悉的 M4.

首先, 如常写 C 头文件代码(设为文件 ht.h.m4), 只是按一种约定, 一种类型的 操作给个共同的函数, 类型名前缀(这不算太坏的编码的规范, 如果不算好), 需 要做为泛型参数的给个名字. 最终, 我期望用 M4 处理一下这文件, 得到, 比如, 哈稀表的宏实现

HT_INIT(name, key_t, val_t, key_hash, key_equal).

其中, name 为结构名, 所有生成的函数结构将都以 name_ 开头. 如 key_hash 原型为

integer key_hash(key_t key)

具体可以是宏, 也可以是函数.

在测试时, 我只要定义好 key_t, val_t, key_hash, key_equal, 再包含头文件 (假设为 ht.h.m4), 就可以测试调用函数, 如

val_t name_get(name_t *p, key_t k).

测试完后, 可以用 m4 -P ht.h.m4 > ht.h 来生成头文件 ht.h, 里面有期望的 HT_INIT 宏. 在用 make 时, 可以添加规则.

%.h: %.h.m4
m4 -I$(srcdir) -P $^ > $(notdir $@)

%.c: %.c.m4
m4 -I$(srcdir) -P $^ > $(notdir $@)

挂羊头, 卖狗肉, 下面给另一个例子.

2 实例

CSEQ, 一个 C 的 sequnce 的实现.

2.1 文件 cpptemps.m4

文件内容如下段, 抽出来, 可以复用.

m4_divert(-1)
m4_define(`CS_QUOTE', `m4_ifelse(`$#', `0', `', ``$*'')')
m4_define(`CS_FOREACH', `m4_pushdef(`$1')_CS_FOREACH($@)m4_popdef(`$1')')
m4_define(`_CS_ARG1', `$1')
m4_define(`_CS_FOREACH', `m4_ifelse(`$2', `()', `',
`m4_define(`$1', _CS_ARG1$2)$3`'$0(`$1',(m4_shift$2), `$3')')')
m4_define(`CS_NO_ARGS',`_CS_NO_ARGS(`$1',`$2', `$'`#', `$'`@')')
m4_define(`_CS_NO_ARGS',`m4_pushdef(`$1',`$2`'m4_ifelse($3,0,,`('`$4'`)')')')
m4_define(`CS_APPEND_BACK_SLASH',`m4_patsubst(`$*',`
.',`\\\&')')
m4_define(`CS_LSTRIP',`m4_patsubst(`$*',`^[
]*')')
m4_define(`CS_DELETE_COMMENT_LINES',`m4_patsubst(`$*',`//[^
]*')')
m4_define(`CS_CASCAT',`CS_APPEND_BACK_SLASH(CS_LSTRIP(CS_DELETE_COMMENT_LINES($@)))')
m4_divert`'m4_dnl

2.2 文件 cseq.h.m4

实现文件, 主题无关的不优雅代码, 所以不全. 其中 defs.h 文件是一些包装内存分配等常见宏的定义.

CSEQ_FUNCS 里都是函数名, 如 size 对应 name_size, 在 CSEQ_INIT(cvs, char) 后, 将生成 cvs_size 函数.

///redundant comment introduced by m4`'m4_ifdef(`CS_FILE_CSEQ_H_M4',`',`m4_define(`CS_FILE_CSEQ_H_M4',`1')
#ifndef FILE_CSEQ_H__
#define FILE_CSEQ_H__ 1

#include "defs.h"
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

__attribute__((__const__))
static inline size_t cseq_cal_resize(size_t n) {
if (n > 134217728UL) {
return n + (n >> 1);
}
return n + n;
}

///m4_ifelse(
/*)
m4_include(cpptemps.m4)
m4_define(`CSEQ_INIT', `m4_ifelse(
CS_NO_ARGS(`name_t', ``$1''`_t')
CS_NO_ARGS(`val_t', ``$2'')
m4_ifelse(m4_eval($# < 3),1,
`CS_NO_ARGS(`CSEQ_FUNC_ATTR', `static inline')',
`CS_NO_ARGS(`CSEQ_FUNC_ATTR', `$3')'
)
m4_pushdef(`CSEQ_FUNCS', (append, array, check_capacity,
free, get, init, new, prepend, realloc, release,
reverse, rmhead, rmtail, set, size, resize, splice, move__,
add, copy, detach, attach))
CS_FOREACH(`x', CSEQ_FUNCS, `CS_NO_ARGS(`name_'x, ``$1_''``''x)')
)m4_dnl*/

typedef struct {
val_t *base;
size_t capacity;
size_t size;
int start;
} name_t;

CSEQ_FUNC_ATTR void name_init(name_t *p) {
memset(p, 0, sizeof(*p));
}

CSEQ_FUNC_ATTR void name_release(name_t *p) {
xfree(p->base);
memset(p, 0, sizeof(*p));
}

CSEQ_FUNC_ATTR name_t* name_new() {
name_t *p = xmalloc(sizeof(name_t));
name_init(p);
return p;
}

CSEQ_FUNC_ATTR void name_free(name_t *p) {
name_release(p);
xfree(p);
}

CSEQ_FUNC_ATTR size_t name_size(name_t *p) {
return p->size;
}

// more bulk such as splice ...

///m4_ifelse(
///m4_popdef(`name_t')
///m4_popdef(`val_t')
///CS_FOREACH(`x', CSEQ_FUNCS, `m4_popdef(`name_'x)')
///m4_popdef(`CSEQ_FUNCS')
///m4_popdef(`CSEQ_FUNC_ATTR')
///)')

#define CSEQ_INIT(name, cval_t) \
CS_CASCAT(CSEQ_INIT(`name``##''',`cval_t'))