祝愿大家身体健康!

 找回密码
 站点注册
查看: 8|回复: 1

[PBIDEA] PBIDEA:用 uo_hash 做增量摘要计算全解(七种算法、分块续算与未知名静默回落 SM3,PB10 基准 · PB12.5 实测)

[复制链接][复制链接]
发表于 1 小时前 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?站点注册

×
PBIDEA:用 uo_hash 做增量摘要计算全解(七种算法、分块续算与未知名静默回落 SM3,PB10 基准 · PB12.5 实测)

阅读说明
① 适用版本:PB 10.0 基准编写,PB 12.5 实机逐项验证(21 组探针两版读数逐字一致,行为零版本差异);PBIDEA 运行库 2026 年 10 月后版本均适用。
② 支持数据库:本文不涉及数据库,全部计算在内存完成。
③ 操作系统与环境要求:Windows 7 及以上;需安装 PBIDEA 运行库(uo_hash 位于 websuite 库,依赖 PbIdea.dll)。
④ 难度系数:★★☆☆☆(接口只有六个声明,难点在「Final 不重置状态」「未知名静默回落 SM3」这两个反直觉行为)。
⑤ 其它阅读说明:本文结论全部来自实机探测——PB10 与 PB12.5 双轨各 21 组用例,摘要值与 Python 标准库 hashlib 逐字节对拍一致;另有 3 组负向编译探测确认不存在的 API。与既有文章的分工:《uo_blob 全解》里的 md5()、crc32() 是对整块 blob 的一次性摘要;SM3/HMAC 篇讲的是带密钥的国密与 HMAC——本篇讲的是标准增量摘要算子 uo_hash 本体。

一、这一篇讲什么

校验一个下载文件是否完整、给口令存个指纹、把大文件分块读完算个 MD5——这类活都叫「摘要计算」。PBIDEA 提供的 uo_hash 是一个标准的增量摘要算子:数据可以一段一段喂进去(update),随时取当前结果(Final),算完关闭(close)。

它一共只有六个声明:open、update(字符串/blob 两个重载)、Final(字符串/blob 两个重载)、close。接口极小,但实测下来有两组行为非常反直觉:Final 取完结果后状态不归零,继续 update 会接着算;open 传一个不认识的算法名,它不报错,静默改算 SM3。这两条踩中任何一条,产出的摘要值就是错的,而且没有任何报错提示。本篇把七个算法、两条输入路、一套状态机、全部边界行为逐一实测摊开。

二、API 全貌:六个声明与返回值语义

声明作用实测要点
open(algo)按算法名创建算子,返回 boolean算法名大小写不敏感;未知名也返回 true(见第八节)
update(string)喂入字符串字符串自动转 UTF-8 后参与计算
update(blob)喂入二进制原样字节参与计算,转什么编码由调用方决定
Final(ref string hex)取十六进制摘要,返回 int返回值=摘要字节数(md5 是 16,不是成功标志)
Final(ref blob hash)取二进制摘要,返回 int同上,blob 长度与返回值一致
close()关闭算子关闭后 Final 返回 0;可重新 open 复用实例


两个容易误解的返回值语义,先钉死:


  • Final 的返回值不是成功/失败标志,是摘要的字节长度:md5 返回 16、sha1 返回 20、sha256 返回 32、sha512 返回 64。它大于 0 只说明「有摘要产出」。
  • 十六进制结果是全大写,长度恒为摘要字节数的两倍:md5 给 32 个字符,sha512 给 128 个字符。


三、最小示例:算一个 MD5
前置:工程已引用 PBIDEA 的 websuite 库。以下代码可放在任意按钮的 clicked 事件或函数里直接跑,无需界面控件。
  1. // 示例输入:待摘要的原始文本
  2. string ls_src
  3. ls_src = 'abc'
  4. // 创建算子并按 md5 算法打开
  5. uo_hash lh
  6. string ls_hex
  7. long li_ret
  8. lh = create uo_hash
  9. lh.open('md5')
  10. lh.update(ls_src)            // 字符串自动按 UTF-8 转字节参与计算
  11. li_ret = lh.Final(ls_hex)    // 返回值 = 摘要字节数(md5 为 16)
  12. lh.close()
  13. destroy lh
  14. // 实测输出:900150983CD24FB0D6963F7D28E17F72(与标准向量一致,全大写)
  15. MessageBox('MD5 摘要', 'ret=' + String(li_ret) + ' hex=' + ls_hex)
复制代码

'abc' 的 MD5 是密码学教材里的标准测试向量,实测输出 900150983CD24FB0D6963F7D28E17F72,与 Python 标准库 hashlib 的计算结果逐字符一致。用这个向量做第一道自检,能立刻确认环境里 PBIDEA 运行库工作正常。

四、七种算法与标准向量

用同一个 'abc' 输入,把七个算法名全部实测一遍,摘要值与 Python hashlib 逐字节对拍:

open() 参数摘要字节数十六进制长度'abc' 摘要实测(前 16 位)
md51632900150983CD24FB0
sha12040A9993E364706816A
sha224285623097D223405D822
sha2563264BA7816BF8F01CFEA
sha3844896CB00753F45A35E8B
sha51264128DDAF35A193617ABA
sm3326466C7F0F462EEEDD9


全部七组与标准值一致,其中 sm3 的 66C7F0F4…… 正是国密 SM3 标准文档里 'abc' 的官方测试向量。算法名大小写不敏感:实测 'MD5'、'SHA256' 都能正确打开并算出对应结果。但注意——open 参数是字符串写错不报错,见第八节。

五、增量的意义:分块 update 与一次性等价

增量摘要的价值在于:数据不必攒成一整块。实测分块与一次性喂养的结果完全等价:
  1. // 示例输入:待摘要的完整内容(演示用 12 字节,实际可任意长)
  2. string ls_full
  3. ls_full = 'abcdefghijkl'
  4. // 两个算子:一个一次喂完,一个分三块喂
  5. uo_hash lh
  6. string ls_hex1
  7. string ls_hex2
  8. lh = create uo_hash
  9. lh.open('md5')
  10. lh.update(ls_full)
  11. lh.Final(ls_hex1)
  12. lh.close()
  13. lh.open('md5')
  14. lh.update('abc')             // 第 1 块
  15. lh.update('def')             // 第 2 块
  16. lh.update('ghijkl')          // 第 3 块
  17. lh.Final(ls_hex2)
  18. lh.close()
  19. destroy lh
  20. // 实测两个结果相等;换成 sha256 分四块同样相等
  21. MessageBox('分块等价性', 'one=' + ls_hex1 + '~r~nchunk=' + ls_hex2)
复制代码

性能上有一笔账要算清。实测对 102400 字节的数据做 MD5:一次性 update 消耗 CPU 时间 16 毫秒;分成 100 块、每块 1 KB 逐块 update,累计 234~250 毫秒——分块调用本身有十几倍的开销。所以分块的意义不是省时间,而是省内存:对大文件、网络流这类不可能整块装进一个字符串的数据,分块是唯一可行的方式;数据本来就能整块装下时,直接一次喂完更快。

六、两条输入路:字符串自动转 UTF-8,blob 是原样字节

update 有字符串和 blob 两个重载,编码语义完全不同,用同一个中文串「中文」实测:
  1. // 示例输入:同一个中文串,走两条输入路
  2. string ls_cn
  3. ls_cn = Char(20013) + Char(25991)    // 「中文」二字
  4. // 路 A:字符串直喂(内部自动转 UTF-8,6 字节)
  5. // 路 B:blob 喂 GBK 字节(EncodingANSI!,4 字节)
  6. uo_hash lh
  7. string ls_hex_a
  8. string ls_hex_b
  9. blob lb_gbk
  10. lh = create uo_hash
  11. lh.open('md5')
  12. lh.update(ls_cn)
  13. lh.Final(ls_hex_a)
  14. lh.close()
  15. lb_gbk = Blob(ls_cn, EncodingANSI!)
  16. lh.open('md5')
  17. lh.update(lb_gbk)
  18. lh.Final(ls_hex_b)
  19. lh.close()
  20. destroy lh
  21. // 实测:A=A7BAC2239F...(与 Python 对 UTF-8 字节算的一致)
  22. //       B=BCCE109775...(与 Python 对 GBK 字节算的一致)
  23. MessageBox('两条输入路', 'str=' + ls_hex_a + '~r~nblob=' + ls_hex_b)
复制代码

三条实测结论:


  • 字符串路自动转 UTF-8。字符串喂进去算出的摘要,与外部工具对 UTF-8 字节算出的完全一致——不管 PB 内部字符串是什么编码,uo_hash 统一按 UTF-8 处理。
  • blob 路是原样字节。喂 GBK 字节就按 GBK 字节算,喂 UTF-8 字节就按 UTF-8 字节算。要和外部系统的摘要对上,必须让两边的字节序列一致——对方用 GBK 算的,你就得喂 EncodingANSI! 的 blob;对方用 UTF-8 算的,喂字符串或 UTF-8 blob 都行。
  • 两条路结果不同。同一个「中文」,字符串路 6 字节、GBK blob 路 4 字节,摘要自然不同。校验对不上号时,第一件事就是核对两边的字节序列。


七、状态机:Final 不重置,重启要 close 加 open

这是本对象最反直觉的一组行为,逐项实测:


  • Final 取完结果,内部状态不归零。同一算子上连取两次 Final,结果相同;再 update 会从原状态继续累计——对已喂 'abc' 的 md5 算子 Final 之后又喂 'def',再 Final 得到的是 md5('abcdef') = E80B5017098950FC58AAD83C8C14978E,不是 md5('def')。
  • 未 open 就 Final:返回 0,ref 参数不被填充(原值原样保留);未 open 就 update:静默忽略,不崩溃不报错。
  • close 之后 Final:同样返回 0、不填充。close 之后可以重新 open,实例可复用,重新开就是全新状态。


于是「用同一个算子连续算多条独立摘要」的正确姿势是每条之间 close 加 open:
  1. // 示例输入:待摘要的字符串清单(模拟三条待校验的数据包内容)
  2. string ls_item1, ls_item2, ls_item3
  3. ls_item1 = 'user=admin&ts=20261104'
  4. ls_item2 = 'payload-7f3a9c'
  5. ls_item3 = 'log-line-088'
  6. // 逐条独立计算 SHA256:第二条起必须先 close 再 open,否则上一条的状态会串进来
  7. uo_hash lh
  8. string ls_hex
  9. string ls_out
  10. long li
  11. lh = create uo_hash
  12. For li = 1 To 3
  13.     If li > 1 Then
  14.         lh.close()
  15.     End If
  16.     lh.open('sha256')
  17.     If li = 1 Then
  18.         lh.update(ls_item1)
  19.     ElseIf li = 2 Then
  20.         lh.update(ls_item2)
  21.     Else
  22.         lh.update(ls_item3)
  23.     End If
  24.     lh.Final(ls_hex)
  25.     ls_out = ls_out + String(li) + ': ' + Left(ls_hex, 24) + '~r~n'
  26. End For
  27. lh.close()
  28. destroy lh
  29. MessageBox('SHA256 清单', ls_out)
复制代码

如果嫌状态管理麻烦,也可以每条摘要都 create 一个新实例、算完 destroy——只是对象创建销毁的开销比 close 加 open 大一些,批量场景建议复用实例。

八、最大的坑:未知名静默回落 SM3

open 的算法名写错了会怎样?实测答案是:不报错、open 照样返回 true,然后按 SM3 计算。四组取证:

open() 实传参数open 返回实际算出说明
'notalgo'(胡写的名字)true66C7F0F4……(SM3)未知名一律回落 SM3
'sha'(不完整的名字)true66C7F0F4……(SM3)没有前缀匹配,匹配不上就回落
'sha3'true66C7F0F4……(SM3)不支持 SHA-3,同样回落
'sha-256'(带连字符)true66C7F0F4……(SM3)正确写法是 'sha256',无连字符


也就是说,算错算法名得到的是一个看起来完全正常的 SM3 摘要——长度正常、格式正常、不报任何错。如果上游系统按 SHA256 校验,这个结果永远对不上,而且你排查时根本想不到算法名写错了。防御办法只有一条:上线前用已知向量自检——按第三节用 'abc' 核对 md5 的标准值,摘要一致才说明算法名写对了;或者干脆在代码里用常量封装算法名,禁止散落硬编码。

九、坑清单(发布前逐条实测核对)


  • Final 的返回值是摘要字节数,不是成功标志;判断有没有产出要看返回值是否大于 0。
  • 十六进制结果全大写;与外部小写摘要比对前先统一大小写。
  • 字符串 update 自动转 UTF-8;要与按 GBK 计算的外部系统对摘要,必须走 blob 路(EncodingANSI!)。
  • blob update 是原样字节,编码选择权完全在调用方。
  • Final 不重置状态:取完结果继续 update 会接着累计('abc' 后接 'def' 等于算 'abcdef')。
  • 连续算多条独立摘要,必须 close 再 open(或换新实例),否则上一条的状态串进下一条。
  • 未 open 就 Final 返回 0 且 ref 参数不填充;判断前先确认 open 成功过。
  • 未 open 就 update 静默忽略——不报错,但你的数据没被算进去。
  • 未知名静默回落 SM3:'notalgo'、'sha'、'sha3'、'sha-256' 全部按 SM3 算,open 照样返回 true。
  • 正确的算法名一共七个:md5、sha1、sha224、sha256、sha384、sha512、sm3;大小写随意,写法无连字符。
  • 分块 update 与一次性喂养结果等价,但分块调用有开销(100 块比一次性慢十几倍),能整块喂就整块喂。
  • 没有 Reset、HexDigest、GetAlgoList 这类方法(负向编译探测确认),「重置」的唯一办法是 close 加 open。
  • close 之后重新 open 完全可用,实例可以长期复用,不必反复 create。


十、PB12.5 差异

21 组用例两版跑完,摘要行为零差异:七个算法的摘要值、大小写不敏感、未知名回落 SM3、Final 不重置、未 open 与 close 后的行为、分块等价性,PB10 与 PB12.5 读数逐字一致。

仅两处与行为无关的探测口径差异,如实记录:一是用「blob 切片转字符串再取 Asc」做字节窥探时,两版对高位字节的读数形态不同(PB12.5 给问号的编码值 63,PB10 给 0),该通道在中文 ANSI 环境本就不可靠,字节级验证应以十六进制摘要输出为准;二是 100 KB 分块压测的 CPU 计时两版各在 234~250 毫秒之间浮动,属机器计时噪声。

十一、实机验证情况


  • PB10:21 组探针全部 PASS;--pb-version 100 全量编译 0 错误;摘要值与 Python hashlib 逐字节一致。
  • PB12.5:同 21 组探针全部 PASS;--pb-version 125 全量编译 0 错误;读数与 PB10 逐字一致。
  • 负向探测 3 组(Reset / HexDigest / GetAlgoList)双轨编译均报 C0051,确认方法不存在。
  • 文中全部代码块即实机验证过的探针等价代码,无未实测段落。


十二、结语

uo_hash 是那种「接口五分钟、边界两小时」的对象:六个声明一眼看完,但 Final 不重置、未知名回落 SM3 这两条,文档不写、报错不出,只能靠实机把边界一个个踩出来。记住三个动作要领——算法名用常量、独立摘要要重启、跨系统对摘要先对字节编码——剩下的就是普通的状态机代码了。
共享共进共赢
Sharing And Win-win Results
SYBASEBBS - 免责申明1、欢迎访问“SYBASEBBS.COM”,本文内容及相关资源来源于网络,版权归版权方所有!本站原创内容版权归本站所有,请勿转载!
2、本文内容仅代表作者观点,不代表本站立场,作者自负,本站资源仅供学习研究,请勿非法使用,否则后果自负!请下载后24小时内删除!
3、本文内容,包括但不限于源码、文字、图片等,仅供参考。本站不对其安全性,正确性等作出保证。但本站会尽量审核会员发表的内容。
4、如本帖侵犯到任何版权问题,请立即告知本站 ,本站将及时删除并致以最深的歉意!客服邮箱:admin@sybasebbs.com
 楼主| 发表于 半小时前 | 显示全部楼层
PB10 兼容版 PBL 下载:

给需要 PB10 环境的朋友打包了文中全部示例代码(nvo_art87 非可视对象,四个方法对应正文四段示例)。

环境要求
- PB 10 基准构建(PB10 全量编译 0 错误),兼容 PB 11/12.x(12.5 已实机验证)
- 需 PBIDEA 运行库(PbIdea.dll + websuite.pbl,uo_hash 所在库)
- Windows XP 及以上;本文不依赖数据库

使用步骤
1) 解压后用 PB 打开 nvo_art87.pbt(或把 nvo_art87.pbl 加入你的工程);
2) 把 websuite.pbl 追加进工程库列表(pbt 内库列表为相对路径,未含各机器的安装路径);
3) 四个方法:of_run1 最小 MD5 示例 / of_run2 分块与一次性等价 / of_run3 字符串路与 GBK blob 路对照 / of_run4 连续三条独立 SHA256 的正确重启姿势;
4) 调用片段:
  1. nvo_art87 lo
  2. string ls
  3. lo = create nvo_art87
  4. ls = lo.of_run1()
  5. destroy lo
  6. MessageBox('MD5', ls)
复制代码

实机验证情况
- PB10:导入 0 错 + 全量编译 0 错 + 4 方法运行 PASS(摘要与标准向量/外部标准库逐字节一致);
- PB12.5:同源 0 错 + 4 方法 + 21 组边界探针全 PASS,读数与 PB10 逐字一致,行为零版本差异;
- 无未实测段落。

三个最容易踩的坑(详见正文):未知名静默回落 SM3('sha-256' 这种写法也算未知名);Final 不重置状态(独立摘要必须 close + open);Final 返回值是摘要字节数不是成功标志。
共享共进共赢
Sharing And Win-win Results
您需要登录后才可以回帖 登录 | 站点注册

本版积分规则

免责声明:
本站所发布的一切破解补丁、注册机和注册信息及软件的解密分析文章仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。

Mail To:Admin@SybaseBbs.com

客服微信:18669893686
挂谷猜想 · 探索

Archiver|PowerBuilder(PB)BBS社区 ( 鲁ICP备2021027222号-1 )

GMT+8, 2026-11-4 06:58 , Processed in 0.031648 second(s), 7 queries , MemCached On.

Powered by Discuz! X5.0

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表