2025熵密杯 -- 初始谜题 -- Reproducibility

前言

本文记录2025熵密杯初始谜题赛题复现过程,参考languag3师傅的熵密杯题解博客。膜拜大佬~

https://languag3.github.io/

初始谜题1

sm4_encrypt.py

import binascii
from pyasn1.codec.der.decoder import decode
from pyasn1.type import univ, namedtype
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from gmssl import sm3, func, sm2
from pyasn1.codec.der.encoder import encode class SM2Cipher(univ.Sequence):
componentType = namedtype.NamedTypes(
namedtype.NamedType('xCoordinate', univ.Integer()), # -- x 分量
namedtype.NamedType('yCoordinate', univ.Integer()), # -- y 分量
namedtype.NamedType('hash', univ.OctetString()), # --哈希值
namedtype.NamedType('cipherText', univ.OctetString()) # -- SM4密钥密文
) class EncryptedData(univ.Sequence):
componentType = namedtype.NamedTypes(
namedtype.NamedType('algorithm', univ.ObjectIdentifier('1.2.156.10197.1.104.2')), # -- SM4-CBC OID
namedtype.NamedType('iv', univ.OctetString()), # -- SM4-CBC加密使用的初始化向量(IV)
namedtype.NamedType('cipherText', univ.OctetString()) # -- SM4加密的密文
) class EnvelopedData(univ.Sequence):
componentType = namedtype.NamedTypes(
namedtype.NamedType('encryptedKey', SM2Cipher()), # -- 使用SM2公钥加密SM4密钥的密文
namedtype.NamedType('encryptedData', EncryptedData()), # -- 使用SM4密钥对明文加密的密文
namedtype.NamedType('digestAlgorithm', univ.ObjectIdentifier('1.2.156.10197.1.401.1')), # -- SM3算法OID
namedtype.NamedType('digest', univ.OctetString()) # -- 对明文计算的摘要值
) def sm4_cbc_encrypt(plaintext: bytes, key: bytes, iv: bytes):
backend = default_backend()
cipher = Cipher(algorithms.SM4(key), modes.CBC(iv), backend=backend) #填充模式 nopadding
encryptor = cipher.encryptor()
ciphertext = encryptor.update(plaintext) + encryptor.finalize()
return ciphertext def sm2_encrypt(plaintext: bytes,public_key:bytes) -> bytes:
sm2_crypt = sm2.CryptSM2(private_key="",public_key=public_key.hex())
ciphertext = sm2_crypt.encrypt(plaintext)
return ciphertext def sm3_hash(text:bytes):
hash_value = sm3.sm3_hash(func.bytes_to_list(text))
return hash_value def read_key_from_file(file_path):
try:
with open(file_path, 'r') as file:
key = file.read().strip()
return key
except FileNotFoundError:
print(f"错误: 文件 {file_path} 未找到。")
except Exception as e:
print(f"错误: 发生了未知错误 {e}。")
return None # 对由abcd组成的字符串加密的方法
def sm4_encrypt(plaintext:str,sm2_public_key: str,sm4_iv:str):
sm4_key = bytes.fromhex(read_key_from_file("key.txt")) #从文件读取固定的key
# sm4
envelope = EnvelopedData()
plaintext_bytes = plaintext.encode('utf-8')
ciphertext = sm4_cbc_encrypt(plaintext_bytes,sm4_key,bytes.fromhex(sm4_iv)) # sm2
encrypted_key = sm2_encrypt(sm4_key,bytes.fromhex(sm2_public_key)) # sm3
digest = sm3_hash(plaintext_bytes) envelope['encryptedData'] = EncryptedData()
envelope['encryptedData']['iv'] = univ.OctetString(bytes.fromhex(sm4_iv))
envelope['encryptedData']['cipherText'] = univ.OctetString(ciphertext) envelope['encryptedKey'] = SM2Cipher()
envelope['encryptedKey']['xCoordinate'] = univ.Integer(int.from_bytes(encrypted_key[:32], 'big'))
envelope['encryptedKey']['yCoordinate'] = univ.Integer(int.from_bytes(encrypted_key[32:64], 'big'))
envelope['encryptedKey']['hash'] = univ.OctetString(encrypted_key[64:96])
envelope['encryptedKey']['cipherText'] = univ.OctetString(encrypted_key[96:]) envelope['digest'] = univ.OctetString(bytes.fromhex(digest))
return encode(envelope).hex() # 从asn1格式的16进制字符串提取参数
def asn1_parse(asn1_hex_str:str,asn1_spec):
# 将16进制字符串转换为字节
der_bytes = binascii.unhexlify(asn1_hex_str)
# 解码为ASN.1对象
enveloped_data, _ = decode(der_bytes, asn1Spec=asn1_spec)
# sm2
sm2_x = hex(int(enveloped_data['encryptedKey']['xCoordinate']))[2:]
sm2_y = hex(int(enveloped_data['encryptedKey']['yCoordinate']))[2:]
sm2_hash = enveloped_data['encryptedKey']['hash'].asOctets().hex()
sm2_ciphertext = enveloped_data['encryptedKey']['cipherText'].asOctets().hex() # sm4
sm4_algorithm = str(enveloped_data['encryptedData']['algorithm'])
sm4_iv = enveloped_data['encryptedData']['iv'].asOctets().hex()
sm4_cipherText = enveloped_data['encryptedData']['cipherText'].asOctets().hex() # sm3
digestAlgorithm = str(enveloped_data['digestAlgorithm'])
digest = enveloped_data['digest'].asOctets().hex() # 输出提取的值
print("asn1格式的16进制字符串:")
print(f" asn1: {asn1_hex_str}")
print("SM2参数:")
print(f" xCoordinate: {sm2_x}")
print(f" yCoordinate: {sm2_y}")
print(f" hash: {sm2_hash}")
print(f" cipherText: {sm2_ciphertext}") print("SM4参数:")
print(f" algorithm: {sm4_algorithm}")
print(f" iv: {sm4_iv}")
print(f" cipherText: {sm4_cipherText}") print("SM3参数:")
print(f" digestAlgorithm: {digestAlgorithm}")
print(f" digest: {digest}") if __name__ == "__main__":
plaintext = "6163616263626161626461646464636361626263626464626361616164636462636462646461646461626462646361636264616364646462646462626261636261646163626463636262616462646462616362616363646463646361616263646261636164636263646163646161636164646364646261626463636462636162636162646261626163636161616463616261646264616162646162626162626462616363616161636362616461626463616462646261626264626464626262636363636162616261626163616164616462626163636164646161646361626363646462626261636261636164646262646362616263636363626461636164646261636361646463616161626164626461636163636461646164616161616163616164636164646261646163626163636164616162636263616461636261646264626263626264636164646263616164626463626461646364616362626261616262616264616361626264636264616461646163626364626462636161636262636163616261616262626362636463616263616364616363626163636363636262646363616464626461616363646361626162636261636364646362626462616364626462626161616264636162626263626462626264646162626462616261616264626161616363636364616263626461636162616462616363616461646363636261636363616162646164626361616464646463646263646363636164626164646463646361636364616261626261646461646463626161616361626161626362626262636164626463636163626163616163636262646463646162616363616364636164646364626464626164626162636161616263646164636461626161636262646463636462646161636462626264626463646364636362626264616362646462636263616361626262616464636263616464616363646163616262616162626261626261616461636361636164636162626461646264636162646363636263616363646161636464626161616462636464646164616361646264616361626263646264616162636164636462616164646163616461646362626464"
sm2_key = "044f66804d1d30f4499377b96dc8e18faab8300ebddf3eb0fa2065214c260d64c08c6dfe7d9923d6d5baa3a0512a2ede03357c723230ebf77906f82dc1b0fccc1e"
iv = "43d4192f9f74e90543d4192f9f74e905"
asn1_hex_str = sm4_encrypt(bytes.fromhex(plaintext).decode('utf-8'),sm2_key,iv)
asn1_parse(asn1_hex_str,EnvelopedData())
(m, c) = (6362646264646264646361636463646261636164626362646264616161636162636261616461646263626362626264636163616264626164616161616361616161636163636361626463616361626263626263646462626261616462646362626162646162646461646264626361646264636164626262626264646464626362626462626364646463636362636264616161636461626363626362616263636264636164636261646262626161636161616461616163616461626461646464616363646462636464646464636462636264636464636162616461646263646264636363616263616164646263646362646161616361646261616161626261626462636261646362626362626462636463616164636263646164636461646364646464616461626463616463636362626464646463636461626264636363636263646463626163636362646264616261636464626164646363626164626264616462626264646463646163646162626364616264646163636362646164616263616461626164636261646361636162616162646161646162636364646162626361646362646361626463616262646362636261616464626162636161616362616464616261626363616464626164616362616262616463626364646461636461636164636261626364646362626263636164626161636264646261636264626164626264636463616263646462646464636461626264626262626263626362636463636362636264646262626162626364616363636264626462636163646361636163616464626361616262646361626261646364646461636164626164646361626264616364626361636163626163636363626464626462646364646364616461616161616361646264626364636364626261636363636361616264636262646262616263636361616361616261616162636163646363646264636364616362626362626463616264646261626164646263646164646264616463646462626261616461636461626262636462646163636461646362616363616163616361626162636462616362636361646363626161636362636361616261636362636463, 308203ec30790220467b2389364b2ebd2eadfa624d668c9b0d530b89edbaf9676c1d7db18c7da40502210099f160a4fc540baa3c316e0b28db789f366fc4c84ba1a98e3aaf0806667a82d804204452c9104b87f44ac026d449cb5b5b5f306b2deab5187db7b3cb845659b1714b0410443c9a3f4f7b0083ddd323d6a1f576503082034006082a811ccf550168020410f414bdfbca9d9e902114536a3b9c443204820320d76516cc03294caaa08df866f53ac3ddc8331f5aa82c09d1ae49c41746165293d70f8d2e7578e4a6c59c9a2adb7b446fd7139e6e1d9f4fef7dca09425b574dfd30c24d773c59bcd86384f013439e7b0f61439192f9b7889a72d4fb59d20eaa7dc7191594aa5cdb855410f0b6be69cb5eb5f303a605300d48c8ca0b86549ba2c586009fa7a24e4b71a2c304aa34f9a6cb45d1e5d97e6d768dd63a1bfbe7a975be13585742aed7e3e606450530c05c0cd1c41dea44603f628bc2398cdd706abb66ca964178122b99c879bf1ded48268f662cbb1d06798e19ecb61c5719fd85b4bf72b25226778632e4510009f101a50fc66ef8f12088357ccac4749262f2eb07b6ea6d800ed3897a02d530b0e6f22b378354edddd8bf4a8df07d020986c1f49570853f4875e00fac96b2f8054288ebebb4d86ebfb644f53b1195fc2ff04effe18d419da605ba949ff7a3cc3624fdb71af173a8b8e0c721ff0095f1cfb08d1f236835013811502da499408de1ed0d329027d65b62790043f0b2f67a71f2056ca1c2e431147b145d8817b86a49d08e82c5d710e999078fa8bc1144bb77654a53d91591522540f3ac835ad1df444f94db2c07f223be6db215034ef39c2123690d0457877c40ea91f5662f2efb57cefa4d8b3bb6ce18bed2de4de3c8d53c9bd2f0be84471a88719f74a3e2193915db3c455c597e897ada24e6c3e628e03416a759ed1a7886525569a2b7e7431311504e27add6b43d85e704d364fe6446c29c314e95c498fea6b8123d811480a1add915a34d4ffeda304e0ee8456f842786a385eaac42c0beec3b37d30906a982340a1a9a0151eca11040fd2c467a8a014e2db9ccec96f0d927208de545fddd0cae8b7da3ce113d0ac95fb84ec74ed266ae8676ac662f21bb93034064a043e5969c378e825b3751d6c54e9d8d79a905c942857103564ce7ed5c61c899420d02170f42e41e3193cd5de116701c59043fc42ec596fc8cd05c75ed9fd514c9323ce01143b84080cc2d81ac477c7c2a9ef97ea0b76159fc2ea24e18c8511046f4e500d1653b16b71f0a1028155173a11c55e777dd13c23e67f9fa5e6a8c32162dcf01b09a3c50c106c63d13ce2a9e2fff014665ec490f947706092a811ccf5501831101042024d458eef08943e27b496fd180de68c18f467bee9d3c802bb56d607a364ddc70)
c = 3081e830790220467b2389364b2ebd2eadfa624d668c9b0d530b89edbaf9676c1d7db18c7da40502210099f160a4fc540baa3c316e0b28db789f366fc4c84ba1a98e3aaf0806667a82d804204452c9104b87f44ac026d449cb5b5b5f306b2deab5187db7b3cb845659b1714b0410443c9a3f4f7b0083ddd323d6a1f57650303e06082a811ccf550168020410b63a85e103d362fb6247c19e324e97c4042098fea6b8123d811480a1add915a34d4fdbbfd86515a457e1b20bf4751ea0010706092a811ccf55018311010420aab05fca300811223b3b957bfe33130770fb7a6b55b030a5809c559344f66f79

analysis

  • 基于当时现场的分析,该题目实现了当前通信中常用的数字信封,使用对称密码加密明文信息,使用非对称密码加密对称密码所使用的密钥,同时利用杂凑函数计算相应的信息摘要。体现在这道题中就是SM4-CBC加密明文信息,SM2加密SM4-CBC密钥,SM3计算消息杂凑值。

  • 本题目中将以下参数加入到asn1证书中,同时明文信息为abcd的组合。

    1. SM2的公钥以及SM2针对于SM4-CBC密钥加密后的密文
    2. SM4-CBC使用的iv以及对明文加密之后的密文
    3. SM3针对于明文进行的杂凑值
    4. 交互得到的一对(m, c)和待解密密文
  • asn1解析结果如下:

    from sm4_encrypt import asn1_parse, EnvelopedData
    
    print("#########################c1 asn_parse")
    c1 = ...
    asn1_parse(c1, EnvelopedData())
    print("#########################c asn_parse")
    c = ...
    asn1_parse(c, EnvelopedData()) """
    #########################c1 asn_parse
    asn1格式的16进制字符串:
    asn1: 308203ec30790220467b2389364b2ebd2eadfa624d668c9b0d530b89edbaf9676c1d7db18c7da40502210099f160a4fc540baa3c316e0b28db789f366fc4c84ba1a98e3aaf0806667a82d804204452c9104b87f44ac026d449cb5b5b5f306b2deab5187db7b3cb845659b1714b0410443c9a3f4f7b0083ddd323d6a1f576503082034006082a811ccf550168020410f414bdfbca9d9e902114536a3b9c443204820320d76516cc03294caaa08df866f53ac3ddc8331f5aa82c09d1ae49c41746165293d70f8d2e7578e4a6c59c9a2adb7b446fd7139e6e1d9f4fef7dca09425b574dfd30c24d773c59bcd86384f013439e7b0f61439192f9b7889a72d4fb59d20eaa7dc7191594aa5cdb855410f0b6be69cb5eb5f303a605300d48c8ca0b86549ba2c586009fa7a24e4b71a2c304aa34f9a6cb45d1e5d97e6d768dd63a1bfbe7a975be13585742aed7e3e606450530c05c0cd1c41dea44603f628bc2398cdd706abb66ca964178122b99c879bf1ded48268f662cbb1d06798e19ecb61c5719fd85b4bf72b25226778632e4510009f101a50fc66ef8f12088357ccac4749262f2eb07b6ea6d800ed3897a02d530b0e6f22b378354edddd8bf4a8df07d020986c1f49570853f4875e00fac96b2f8054288ebebb4d86ebfb644f53b1195fc2ff04effe18d419da605ba949ff7a3cc3624fdb71af173a8b8e0c721ff0095f1cfb08d1f236835013811502da499408de1ed0d329027d65b62790043f0b2f67a71f2056ca1c2e431147b145d8817b86a49d08e82c5d710e999078fa8bc1144bb77654a53d91591522540f3ac835ad1df444f94db2c07f223be6db215034ef39c2123690d0457877c40ea91f5662f2efb57cefa4d8b3bb6ce18bed2de4de3c8d53c9bd2f0be84471a88719f74a3e2193915db3c455c597e897ada24e6c3e628e03416a759ed1a7886525569a2b7e7431311504e27add6b43d85e704d364fe6446c29c314e95c498fea6b8123d811480a1add915a34d4ffeda304e0ee8456f842786a385eaac42c0beec3b37d30906a982340a1a9a0151eca11040fd2c467a8a014e2db9ccec96f0d927208de545fddd0cae8b7da3ce113d0ac95fb84ec74ed266ae8676ac662f21bb93034064a043e5969c378e825b3751d6c54e9d8d79a905c942857103564ce7ed5c61c899420d02170f42e41e3193cd5de116701c59043fc42ec596fc8cd05c75ed9fd514c9323ce01143b84080cc2d81ac477c7c2a9ef97ea0b76159fc2ea24e18c8511046f4e500d1653b16b71f0a1028155173a11c55e777dd13c23e67f9fa5e6a8c32162dcf01b09a3c50c106c63d13ce2a9e2fff014665ec490f947706092a811ccf5501831101042024d458eef08943e27b496fd180de68c18f467bee9d3c802bb56d607a364ddc70
    SM2参数:
    xCoordinate: 467b2389364b2ebd2eadfa624d668c9b0d530b89edbaf9676c1d7db18c7da405
    yCoordinate: 99f160a4fc540baa3c316e0b28db789f366fc4c84ba1a98e3aaf0806667a82d8
    hash: 4452c9104b87f44ac026d449cb5b5b5f306b2deab5187db7b3cb845659b1714b
    cipherText: 443c9a3f4f7b0083ddd323d6a1f57650
    SM4参数:
    algorithm: 1.2.156.10197.1.104.2
    iv: f414bdfbca9d9e902114536a3b9c4432
    cipherText: d76516cc03294caaa08df866f53ac3ddc8331f5aa82c09d1ae49c41746165293d70f8d2e7578e4a6c59c9a2adb7b446fd7139e6e1d9f4fef7dca09425b574dfd30c24d773c59bcd86384f013439e7b0f61439192f9b7889a72d4fb59d20eaa7dc7191594aa5cdb855410f0b6be69cb5eb5f303a605300d48c8ca0b86549ba2c586009fa7a24e4b71a2c304aa34f9a6cb45d1e5d97e6d768dd63a1bfbe7a975be13585742aed7e3e606450530c05c0cd1c41dea44603f628bc2398cdd706abb66ca964178122b99c879bf1ded48268f662cbb1d06798e19ecb61c5719fd85b4bf72b25226778632e4510009f101a50fc66ef8f12088357ccac4749262f2eb07b6ea6d800ed3897a02d530b0e6f22b378354edddd8bf4a8df07d020986c1f49570853f4875e00fac96b2f8054288ebebb4d86ebfb644f53b1195fc2ff04effe18d419da605ba949ff7a3cc3624fdb71af173a8b8e0c721ff0095f1cfb08d1f236835013811502da499408de1ed0d329027d65b62790043f0b2f67a71f2056ca1c2e431147b145d8817b86a49d08e82c5d710e999078fa8bc1144bb77654a53d91591522540f3ac835ad1df444f94db2c07f223be6db215034ef39c2123690d0457877c40ea91f5662f2efb57cefa4d8b3bb6ce18bed2de4de3c8d53c9bd2f0be84471a88719f74a3e2193915db3c455c597e897ada24e6c3e628e03416a759ed1a7886525569a2b7e7431311504e27add6b43d85e704d364fe6446c29c314e95c498fea6b8123d811480a1add915a34d4ffeda304e0ee8456f842786a385eaac42c0beec3b37d30906a982340a1a9a0151eca11040fd2c467a8a014e2db9ccec96f0d927208de545fddd0cae8b7da3ce113d0ac95fb84ec74ed266ae8676ac662f21bb93034064a043e5969c378e825b3751d6c54e9d8d79a905c942857103564ce7ed5c61c899420d02170f42e41e3193cd5de116701c59043fc42ec596fc8cd05c75ed9fd514c9323ce01143b84080cc2d81ac477c7c2a9ef97ea0b76159fc2ea24e18c8511046f4e500d1653b16b71f0a1028155173a11c55e777dd13c23e67f9fa5e6a8c32162dcf01b09a3c50c106c63d13ce2a9e2fff014665ec490f9477
    SM3参数:
    digestAlgorithm: 1.2.156.10197.1.401.1
    digest: 24d458eef08943e27b496fd180de68c18f467bee9d3c802bb56d607a364ddc70
    #########################c asn_parse
    asn1格式的16进制字符串:
    asn1: 3081e830790220467b2389364b2ebd2eadfa624d668c9b0d530b89edbaf9676c1d7db18c7da40502210099f160a4fc540baa3c316e0b28db789f366fc4c84ba1a98e3aaf0806667a82d804204452c9104b87f44ac026d449cb5b5b5f306b2deab5187db7b3cb845659b1714b0410443c9a3f4f7b0083ddd323d6a1f57650303e06082a811ccf550168020410b63a85e103d362fb6247c19e324e97c4042098fea6b8123d811480a1add915a34d4fdbbfd86515a457e1b20bf4751ea0010706092a811ccf55018311010420aab05fca300811223b3b957bfe33130770fb7a6b55b030a5809c559344f66f79
    SM2参数:
    xCoordinate: 467b2389364b2ebd2eadfa624d668c9b0d530b89edbaf9676c1d7db18c7da405
    yCoordinate: 99f160a4fc540baa3c316e0b28db789f366fc4c84ba1a98e3aaf0806667a82d8
    hash: 4452c9104b87f44ac026d449cb5b5b5f306b2deab5187db7b3cb845659b1714b
    cipherText: 443c9a3f4f7b0083ddd323d6a1f57650
    SM4参数:
    algorithm: 1.2.156.10197.1.104.2
    iv: b63a85e103d362fb6247c19e324e97c4
    cipherText: 98fea6b8123d811480a1add915a34d4fdbbfd86515a457e1b20bf4751ea00107
    SM3参数:
    digestAlgorithm: 1.2.156.10197.1.401.1
    digest: aab05fca300811223b3b957bfe33130770fb7a6b55b030a5809c559344f66f79
    """
  • 针对于此等加密,刚学完课程《密码学概论》的我,当前安全且高效的通信加密技术,赛场上看着这道题非常怅惘。后续看到大佬的解题思路如下:针对于国密算法以及数字信封的实现上而言,我们很难找到其漏洞点,但是出了穷举攻击以外,可能出现的漏洞点就在于分组密码的CBC工作模式。由于给出的明密文对的长度很长,而我们要恢复的密文却很短。cipherText = "98fea6b8123d811480a1add915a34d4fdbbfd86515a457e1b20bf4751ea00107"

  • 看到languag3师傅的博客,同时验证待解密密文的第一组密文在明密文对的密文中存在,那么可以判断此处在黑盒处理之前,两者对应的分组的keyinput是相同的,则通过异或处理可以恢复明文的第一块。但是并不能推出该组黑盒使用的key以及其他分组的keyinput的关系。

  • 此后怎对于后续的16字节进行爆破,即为32bit的abcd爆破。

exp

# part1 Solve
from pwn import xor
m1 = "6362646264646264646361636463646261636164626362646264616161636162636261616461646263626362626264636163616264626164616161616361616161636163636361626463616361626263626263646462626261616462646362626162646162646461646264626361646264636164626262626264646464626362626462626364646463636362636264616161636461626363626362616263636264636164636261646262626161636161616461616163616461626461646464616363646462636464646464636462636264636464636162616461646263646264636363616263616164646263646362646161616361646261616161626261626462636261646362626362626462636463616164636263646164636461646364646464616461626463616463636362626464646463636461626264636363636263646463626163636362646264616261636464626164646363626164626264616462626264646463646163646162626364616264646163636362646164616263616461626164636261646361636162616162646161646162636364646162626361646362646361626463616262646362636261616464626162636161616362616464616261626363616464626164616362616262616463626364646461636461636164636261626364646362626263636164626161636264646261636264626164626264636463616263646462646464636461626264626262626263626362636463636362636264646262626162626364616363636264626462636163646361636163616464626361616262646361626261646364646461636164626164646361626264616364626361636163626163636363626464626462646364646364616461616161616361646264626364636364626261636363636361616264636262646262616263636361616361616261616162636163646363646264636364616362626362626463616264646261626164646263646164646264616463646462626261616461636461626262636462646163636461646362616363616163616361626162636462616362636361646363626161636362636361616261636362636463"
c1 = "d76516cc03294caaa08df866f53ac3ddc8331f5aa82c09d1ae49c41746165293d70f8d2e7578e4a6c59c9a2adb7b446fd7139e6e1d9f4fef7dca09425b574dfd30c24d773c59bcd86384f013439e7b0f61439192f9b7889a72d4fb59d20eaa7dc7191594aa5cdb855410f0b6be69cb5eb5f303a605300d48c8ca0b86549ba2c586009fa7a24e4b71a2c304aa34f9a6cb45d1e5d97e6d768dd63a1bfbe7a975be13585742aed7e3e606450530c05c0cd1c41dea44603f628bc2398cdd706abb66ca964178122b99c879bf1ded48268f662cbb1d06798e19ecb61c5719fd85b4bf72b25226778632e4510009f101a50fc66ef8f12088357ccac4749262f2eb07b6ea6d800ed3897a02d530b0e6f22b378354edddd8bf4a8df07d020986c1f49570853f4875e00fac96b2f8054288ebebb4d86ebfb644f53b1195fc2ff04effe18d419da605ba949ff7a3cc3624fdb71af173a8b8e0c721ff0095f1cfb08d1f236835013811502da499408de1ed0d329027d65b62790043f0b2f67a71f2056ca1c2e431147b145d8817b86a49d08e82c5d710e999078fa8bc1144bb77654a53d91591522540f3ac835ad1df444f94db2c07f223be6db215034ef39c2123690d0457877c40ea91f5662f2efb57cefa4d8b3bb6ce18bed2de4de3c8d53c9bd2f0be84471a88719f74a3e2193915db3c455c597e897ada24e6c3e628e03416a759ed1a7886525569a2b7e7431311504e27add6b43d85e704d364fe6446c29c314e95c498fea6b8123d811480a1add915a34d4ffeda304e0ee8456f842786a385eaac42c0beec3b37d30906a982340a1a9a0151eca11040fd2c467a8a014e2db9ccec96f0d927208de545fddd0cae8b7da3ce113d0ac95fb84ec74ed266ae8676ac662f21bb93034064a043e5969c378e825b3751d6c54e9d8d79a905c942857103564ce7ed5c61c899420d02170f42e41e3193cd5de116701c59043fc42ec596fc8cd05c75ed9fd514c9323ce01143b84080cc2d81ac477c7c2a9ef97ea0b76159fc2ea24e18c8511046f4e500d1653b16b71f0a1028155173a11c55e777dd13c23e67f9fa5e6a8c32162dcf01b09a3c50c106c63d13ce2a9e2fff014665ec490f9477"
c = "98fea6b8123d811480a1add915a34d4fdbbfd86515a457e1b20bf4751ea00107"
iv1 = "f414bdfbca9d9e902114536a3b9c4432"
iv = "b63a85e103d362fb6247c19e324e97c4"
# print(len(c)) 64 -- 分组后为2组
c_list = [c[32 * i:32 * (i + 1)] for i in range(2)]
# print(c_list)
# print(c1.index(c_list[0])); print(c1.index(c_list[1])) 1088 Error
# Black Cipher Encrypt之前的结果相同
temp = bytes.fromhex(m1[1088:1088 + 32])
c1_block = bytes.fromhex(c1[1088 - 32:1088])
iv = bytes.fromhex(iv)
m_part1 = xor(xor(c1_block, temp), iv)
# print(m_part1) adcddbbadcacabad
// part2 Solve
#include <iostream>
#include <vector>
#include <string>
#include <thread>
#include <chrono>
#include <cstring>
#include <openssl/evp.h>
#include <mutex>
#include <condition_variable> using namespace std; // 线程通信全局变量
bool found = false;
mutex mtx; // 信号量
condition_variable cv; // 字符集以及前16个字节内容固定
const unsigned char char_set[] = "adcddbbadcacabad";
const unsigned char char_map[] = "abcd";
const string tar_hex = "aab05fca300811223b3b957bfe33130770fb7a6b55b030a5809c559344f66f79"; // 生成索引对应的16字节字符
void index_to_char(uint64_t index, unsigned char *result)
{
int len = 0;
while (len < 15)
{
result[len++] = char_map[index % 4];
index /= 4;
}
result[len] = char_map[index];
} // 有结果后终止线程
bool should_terminate()
{
lock_guard<mutex> lock(mtx);
return found;
} // 线程
void thread_worker(uint64_t j)
{
const size_t prefix_len = strlen(reinterpret_cast<const char *>(char_set));
vector<unsigned char> data(prefix_len + 16);
memcpy(data.data(), char_set, prefix_len); unsigned char hash[32];
char hash_hex[65];
hash_hex[64] = '\0'; for (uint64_t i = 0; i <= 0xffffff; ++i)
{
if (should_terminate())
{
return;
} const uint64_t index = (j << 24) + i;
unsigned char suffix[16]; index_to_char(index, suffix);
memcpy(data.data() + prefix_len, suffix, 16); // 计算SM3哈希
EVP_MD_CTX *ctx = EVP_MD_CTX_new();
EVP_DigestInit_ex(ctx, EVP_sm3(), nullptr);
EVP_DigestUpdate(ctx, data.data(), data.size());
EVP_DigestFinal_ex(ctx, hash, nullptr);
EVP_MD_CTX_free(ctx); // 转换哈希结果为十六进制字符串
for (int k = 0; k < 32; ++k)
{
snprintf(hash_hex + 2 * k, 3, "%02x", hash[k]);
} // 检查是否匹配目标哈希
if (tar_hex == hash_hex)
{
// 找到匹配项,设置全局标志并通知主线程
{
lock_guard<mutex> lock(mtx);
found = true;
cout << "Found: " << string(reinterpret_cast<char *>(data.data()), data.size()) << endl;
}
cv.notify_one(); // 通知主线程
return;
}
}
} int main()
{
auto start_time = chrono::high_resolution_clock::now(); vector<thread> threads;
threads.reserve(256); // 创建线程
for (uint64_t j = 0; j <= 0xff; ++j)
{
threads.emplace_back(thread_worker, j);
} // 等待找到结果或所有线程完成
unique_lock<mutex> lock(mtx);
cv.wait(lock, []
{ return found; }); // 找到结果后终止线程
for (auto &t : threads)
{
if (t.joinable())
{
t.join();
}
} // 计算并输出耗时
auto end_time = chrono::high_resolution_clock::now();
chrono::duration<double> elapsed = end_time - start_time;
cout << "Time taken: " << elapsed.count() << " seconds" << endl; return 0;
}
// flag{adcddbbadcacabadcbbbbaaaabcbcabd}

初始谜题2

sm2_verify

import binascii
from datetime import datetime
from pyasn1.type import univ, namedtype
from pyasn1.codec.der.encoder import encode
from pyasn1.codec.der.decoder import decode
from gmssl import sm2
from pyasn1.codec.der import decoder, encoder
from pyasn1_modules import rfc2459
from gmssl.sm2 import CryptSM2
from pyasn1.type.useful import GeneralizedTime
from pyasn1.type.univ import Sequence
from pyasn1.type import useful class ECPrimeFieldConfig(univ.Sequence):
componentType = namedtype.NamedTypes(
namedtype.NamedType('fieldType', univ.ObjectIdentifier('1.2.840.10045.1.1')), # Prime field OID
namedtype.NamedType('prime', univ.Integer()), # Prime number p
) class ECCurveParameters(univ.Sequence):
componentType = namedtype.NamedTypes(
namedtype.NamedType('coefficientA', univ.OctetString()), # Curve coefficient a
namedtype.NamedType('coefficientB', univ.OctetString()), # Curve coefficient b
) class ECDomainParameters(univ.Sequence):
componentType = namedtype.NamedTypes(
namedtype.NamedType('version', univ.Integer(1)), # Version number (1)
namedtype.NamedType('fieldParameters', ECPrimeFieldConfig()), # Field parameters 包含参数oid,p
namedtype.NamedType('curveParameters', ECCurveParameters()), # Curve parameters 包含参数a,b
namedtype.NamedType('basePoint', univ.OctetString()), # Base point G 基点
namedtype.NamedType('order', univ.Integer()), # Order n of base point 参数n
namedtype.NamedType('cofactor', univ.Integer(1)), # Cofactor 余因子 固定值为1
) class SM2SignatureValue(univ.Sequence):
componentType = namedtype.NamedTypes(
namedtype.NamedType('r', univ.Integer()), # First part of signature
namedtype.NamedType('s', univ.Integer()), # Second part of signature
) class SM2SignedData(univ.Sequence):
componentType = namedtype.NamedTypes(
# version
namedtype.NamedType('version', univ.Integer()),
# 哈希算法 OID(SM3)
namedtype.NamedType('digestAlgorithms', univ.ObjectIdentifier()),
# 签名值 r, s
namedtype.NamedType('sm2Signature', SM2SignatureValue()),
# 曲线参数
namedtype.NamedType('ecDomainParameters', ECDomainParameters()),
# 证书
namedtype.NamedType('certificate', univ.OctetString()),
# 签名时间
namedtype.NamedType('timestamp', GeneralizedTime()),
) # 输入值全部为16进制字符串,g为x,y坐标的16进制字符串进行拼接
# p,a,b,n,g对应曲线参数;r,s为签名值的两部分
def asn1_package(version, oid, signature, curve_params, cert_hex, time_stamp):
sm2_signed_data = SM2SignedData()
# version
sm2_signed_data['version'] = version
# 哈希算法 OID(SM3)
sm2_signed_data['digestAlgorithms'] = oid
# 签名值 r, s
sm2_signed_data["sm2Signature"] = SM2SignatureValue()
sm2_signed_data["sm2Signature"]['r'] = int(signature[:64], 16)
sm2_signed_data["sm2Signature"]['s'] = int(signature[64:], 16)
# 曲线参数
sm2_signed_data["ecDomainParameters"] = ECDomainParameters()
sm2_signed_data["ecDomainParameters"]["fieldParameters"] = ECPrimeFieldConfig()
sm2_signed_data["ecDomainParameters"]["fieldParameters"]["prime"] = int(curve_params['p'], 16)
sm2_signed_data["ecDomainParameters"]["curveParameters"] = ECCurveParameters()
sm2_signed_data["ecDomainParameters"]["curveParameters"]["coefficientA"] = univ.OctetString(
bytes.fromhex(curve_params['a']))
sm2_signed_data["ecDomainParameters"]["curveParameters"]["coefficientB"] = univ.OctetString(
bytes.fromhex(curve_params['b']))
sm2_signed_data["ecDomainParameters"]['basePoint'] = univ.OctetString(bytes.fromhex('04' + curve_params['g']))
sm2_signed_data["ecDomainParameters"]['order'] = int(curve_params['n'], 16)
# 证书
sm2_signed_data["certificate"] = univ.OctetString(bytes.fromhex(cert_hex))
# 时间
dt = datetime.strptime(time_stamp, "%Y-%m-%d %H:%M:%S")
asn1_time_str = dt.strftime("%Y%m%d%H%M%SZ")
sm2_signed_data["timestamp"] = GeneralizedTime(asn1_time_str)
return encode(sm2_signed_data).hex() class Sm2CertVerifier:
def __init__(self, cert_hex: str):
ca_pubkey = "8E1860588D9900C16BD19A0FE0A5ACC600224DBD794FFD34179E03698D52421F46E6D8C6E8AADE512C7B543395AC39C76384726C7F8BA537ABCA0C129ECD9882"
self.sm2_crypt = sm2.CryptSM2(public_key=ca_pubkey, private_key=None)
self.cert_tbs, self.signature_bytes, self.cert = self.parse_cert(bytes.fromhex(cert_hex)) @staticmethod
def parse_cert(cert_der_bytes: bytes):
cert, _ = decoder.decode(cert_der_bytes, asn1Spec=rfc2459.Certificate())
tbs = cert.getComponentByName('tbsCertificate')
signature_bytes = cert.getComponentByName('signatureValue').asOctets()
return tbs, signature_bytes, cert # 获取签名值
def decode_rs_from_der(self, signature: bytes) -> bytes:
seq, _ = decode(signature, asn1Spec=Sequence())
r = int(seq[0])
s = int(seq[1])
r_bytes = r.to_bytes(32, byteorder='big')
s_bytes = s.to_bytes(32, byteorder='big')
return r_bytes + s_bytes def verify_signature(self, signature: bytes, tbs: str):
inter_cert_tbs_der = encoder.encode(tbs)
inter_signature = self.decode_rs_from_der(signature)
# 验证签名(tbs_der必须完整,签名必须64字节)
return self.sm2_crypt.verify_with_sm3(inter_signature.hex(), inter_cert_tbs_der) def verify_certificate_expiration_date(self, tbs):
validity = tbs.getComponentByName('validity')
not_before = validity.getComponentByName('notBefore').getComponent()
not_after = validity.getComponentByName('notAfter').getComponent() # 处理 UTCTime 和 GeneralizedTime 两种类型
if isinstance(not_before, useful.UTCTime):
not_before_time = datetime.strptime(str(not_before), "%y%m%d%H%M%SZ")
elif isinstance(not_before, useful.GeneralizedTime):
not_before_time = datetime.strptime(str(not_before), "%Y%m%d%H%M%SZ")
else:
raise ValueError("Unsupported notBefore time format") if isinstance(not_after, useful.UTCTime):
not_after_time = datetime.strptime(str(not_after), "%y%m%d%H%M%SZ")
elif isinstance(not_after, useful.GeneralizedTime):
not_after_time = datetime.strptime(str(not_after), "%Y%m%d%H%M%SZ")
else:
raise ValueError("Unsupported notAfter time format") now = datetime.now()
return not_before_time <= now <= not_after_time def verify(self):
# 验证中间证书有效期
if not self.verify_certificate_expiration_date(self.cert_tbs):
print("证书已过期或尚未生效")
return False
# 验证中间证书签名
if not self.verify_signature(self.signature_bytes, self.cert_tbs):
print("证书验证未通过")
return False
return True class SM2Config:
# sm2参数初始化
def __init__(self, asn1_str):
self.sm2_signed_data,asn1_acess = self.hex_to_asn1(asn1_str, SM2SignedData())
if len(asn1_acess) != 0:
raise ValueError("asn1长度有问题")
cert_hex = self.get_hex_value(self.sm2_signed_data['certificate'])
sm2_cert_verifier = Sm2CertVerifier(cert_hex)
valid = sm2_cert_verifier.verify()
if not valid:
raise TypeError("证书验证不通过")
g = self.get_hex_value(self.sm2_signed_data['ecDomainParameters']['basePoint'])
g = g[2:] if g.startswith("04") else g
self.ecc_table = {
'n': self.get_hex_value(self.sm2_signed_data['ecDomainParameters']['order']),
'p': self.get_hex_value(self.sm2_signed_data['ecDomainParameters']['fieldParameters']['prime']),
'g': g,
'a': self.get_hex_value(self.sm2_signed_data['ecDomainParameters']['curveParameters']['coefficientA']),
'b': self.get_hex_value(self.sm2_signed_data['ecDomainParameters']['curveParameters']['coefficientB']),
}
public_key = self.extract_public_key(sm2_cert_verifier.cert_tbs)
self.sm2_crypt = CryptSM2(
private_key="",
public_key=public_key,
ecc_table=self.ecc_table
)
self.sign = (int(self.sm2_signed_data['sm2Signature']['r']).to_bytes(32, 'big').hex().upper() +
int(self.sm2_signed_data['sm2Signature']['s']).to_bytes(32, 'big').hex().upper()) @staticmethod
def hex_to_asn1(hex_str, asn1_spec):
"""
将16进制字符串转换回ASN.1对象
:param hex_str: 16进制字符串
:param asn1_spec: ASN.1结构定义
:return: ASN.1对象
"""
# 将16进制字符串转换为字节
der_bytes = binascii.unhexlify(hex_str) # 解码为ASN.1对象
asn1_object, excess = decode(der_bytes, asn1Spec=asn1_spec) return asn1_object,excess @staticmethod
def get_hex_value(value):
"""通用转换函数:将 ASN.1 值转换为 16 进制字符串(大写,无前缀)"""
if isinstance(value, univ.Integer):
return format(int(value), 'X') # Integer -> 直接转十六进制
elif isinstance(value, univ.OctetString):
return value.asOctets().hex().upper() # OctetString -> 字节转十六进制
else:
raise TypeError(f"Unsupported type: {type(value)}") @staticmethod
def extract_public_key(tbs):
spki = tbs.getComponentByName('subjectPublicKeyInfo')
public_key_bitstring = spki.getComponentByName('subjectPublicKey')
# 提取位串内容(包含开头的 0x04)
pubkey_bytes = bytearray(public_key_bitstring.asOctets())
# 转成十六进制字符串
return pubkey_bytes.hex() def verify_misc(self):
if (int(self.sm2_signed_data['version']) != 1 or
str(self.sm2_signed_data['digestAlgorithms']) != '1.2.156.10197.1.401.1' or
str(self.sm2_signed_data['timestamp']) != "20250520101000Z"):
return False
return True # sm2验签
def verify(self, data):
valid = self.verify_misc()
if not valid:
return valid
valid = self.sm2_crypt.verify_with_sm3(self.sign, data)
return valid # 通过该函数可以产生一个合法的SM2SignedData
def generateSM2SignedDataExample():
# 版本
version = 1
# 哈希算法oid
oid = '1.2.156.10197.1.401.1'
# 签名值r, s
signature = '6f8eaff551d0f3fa6de74b75b33e1e58f9fdb4dc58e61c82e11e717ffcf168c4db3d5a90ff3625d12b8b658f8dbab34340c278b412b3aff25489e7feb1c75598'
r = signature[:64]
s = signature[64:]
# 曲线参数
curve_params = {
"n": 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123',
"p": 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF',
"g": '32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7bc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0',
"a": 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC',
"b": '28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93',
}
# 证书
cert_hex = '3082017C30820122A003020102020D00947C8427D3E849B48A7E5136300A06082A811CCF550183753036310B300906035504061302434E31133011060355040A130A5368616E674D6942656931123010060355040313095368616E674D694341301E170D3235303532303035353330365A170D3330303531393035353330365A304D310B300906035504061302434E3110300E060355040A1307496E746572434131173015060355040B130E5368616E674D6942656932303235311330110603550403130A7368616E676D696265693059301306072A8648CE3D020106082A811CCF5501822D03420004CECC0005AED684A1E7E39C316E7F3F39BDD0490936BC0E1AFDDC1B9627A05B4418809E5327746EE1977913F036EF0A9A255C27D73C00E45D0BB205B34D2C80D4300A06082A811CCF5501837503480030450220360779CBF5AA6E5E9CC073D95E22C52C09E81CFC06A3916559063A3C8C1DFDE6022100ED0E5E5E51F3894A3EAC11F247739D9F6A88C961D89F68337972BC3CC6BB6706' # 证书16进制格式
# 时间
time_stamp = '2025-05-20 10:10:00' # asn1封装
asn1_package_hex = asn1_package(version, oid, signature, curve_params, cert_hex, time_stamp)
return(asn1_package_hex) if __name__ == '__main__':
# 验签
data = b"Hello, CryptoCup!"
asn1_package_hex = generateSM2SignedDataExample()
sm2_config = SM2Config(asn1_package_hex)
result = sm2_config.verify(data)
print(result)

SM2SignedData

308202CD02010106092A811CCF5501831101304502206F8EAFF551D0F3FA6DE74B75B33E1E58F9FDB4DC58E61C82E11E717FFCF168C4022100DB3D5A90FF3625D12B8B658F8DBAB34340C278B412B3AFF25489E7FEB1C755983081E0020101302C06072A8648CE3D0101022100FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF30440420FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC042028E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E9304410432C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0022100FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123020101048201803082017C30820122A003020102020D00947C8427D3E849B48A7E5136300A06082A811CCF550183753036310B300906035504061302434E31133011060355040A130A5368616E674D6942656931123010060355040313095368616E674D694341301E170D3235303532303035353330365A170D3330303531393035353330365A304D310B300906035504061302434E3110300E060355040A1307496E746572434131173015060355040B130E5368616E674D6942656932303235311330110603550403130A7368616E676D696265693059301306072A8648CE3D020106082A811CCF5501822D03420004CECC0005AED684A1E7E39C316E7F3F39BDD0490936BC0E1AFDDC1B9627A05B4418809E5327746EE1977913F036EF0A9A255C27D73C00E45D0BB205B34D2C80D4300A06082A811CCF5501837503480030450220360779CBF5AA6E5E9CC073D95E22C52C09E81CFC06A3916559063A3C8C1DFDE6022100ED0E5E5E51F3894A3EAC11F247739D9F6A88C961D89F68337972BC3CC6BB6706180F32303235303532303130313030305A

analysis

  • 题目中实现了一个SM2的签名系统,而我们获取得到flag的关键就在于对指定信息进行签名通过交互验签程序的比对。因此task就转移到我们怎么写一个能通过验签函数的签名函数。注:证书为固定内容,也就意味着公钥固定不能进行修改。

  • 针对于签名函数而言,我们查看gmssl.sm2源码之后发现,签名需要找到私钥,但是目前我们只知道公钥以及其签名时使用的基点G以及经过私钥计算之后的公钥,根据sm2的签名过程中的密钥生成方式:

    \[确定a,b,p后选择点P作为基点,选择d且计算Q=d*P,其中d为私钥,P为公钥。
    \]
  • 因此我们自己伪造私钥即可,但是需要满足私钥和公钥的关系,那么我们在曲线上找到我们所选择的私钥d的逆元点即可进行签名验证。

exp

# 伪造私钥,确定相应私钥的基点
from sage.all import * curve_params = {
"n": 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123',
"p": 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF',
"a": 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC',
"b": '28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93',
} pk = "8E1860588D9900C16BD19A0FE0A5ACC600224DBD794FFD34179E03698D52421F46E6D8C6E8AADE512C7B543395AC39C76384726C7F8BA537ABCA0C129ECD9882" p = int(curve_params['p'], 16)
a = int(curve_params['a'], 16)
b = int(curve_params['b'], 16)
n = int(curve_params['n'], 16)
E = EllipticCurve(GF(p), [a, b]) Q = E(int(pk[:64], 16), int(pk[64:], 16)) sk = 2
G = Q * inverse_mod(sk, n) print(G)
# (69820663585833773923605819432869967907739933733211104912986148573209369246123 : 76928629769251499992251494960564507419350106546363535365017317636758007136702 : 1)
# 利用伪造私钥和基点仿造签名
from gmssl.sm2 import CryptSM2 curve_params = {
"n": 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123',
"p": 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF',
"g": "9a5d185c2d305a22641a68c2c637924ae27921e02019213d08788bffa8bff9abaa140fbb0a66d0166463e801113b9ef38198f2918e053902bb67b018476341be",
"a": 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC',
"b": '28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93',
} sm2_crypt = CryptSM2(private_key = "2", public_key = "cecc0005aed684a1e7e39c316e7f3f39bdd0490936bc0e1afddc1b9627a05b4418809e5327746ee1977913f036ef0a9a255c27d73c00e45d0bb205b34d2c80d4", ecc_table=curve_params) data = b'EUWJSFTFHGQEQVRXZHJYKPUXDPUMRDQQ'
signature = sm2_crypt.sign_with_sm3(data)
print(f"signature: {signature}")
# signature: c0f36fe86a81a061139252f518b51651397d517799f30e3dece0acb762f497c3693297c721cbb0d778856e2c2b4cea9cfe0af6a4548f4c29be03688f071fe4f6

初始谜题3

xmss_verify.py

from typing import List, Callable
from hashlib import sha256 def hex_to_32byte_chunks(hex_str):
# 确保十六进制字符串长度是64的倍数(因为32字节 = 64个十六进制字符)
if len(hex_str) % 64 != 0:
raise ValueError("十六进制字符串长度必须是64的倍数") # 每64个字符分割一次,并转换为字节
return [bytes.fromhex(hex_str[i:i + 64]) for i in range(0, len(hex_str), 64)] def openssl_sha256(message: bytes) -> bytes:
return sha256(message).digest() class WOTSPLUS:
def __init__(
self,
w: int = 16, # Winternitz 参数,控制空间与时间的复杂度
hashfunction: Callable = openssl_sha256, # 哈希函数
digestsize: int = 256, # 摘要大小,单位为比特
pubkey: List[bytes] = None,
) -> None:
self.w = w
if not (2 <= w <= (1 << digestsize)):
raise ValueError("规则错误:2 <= w <= 2^digestsize")
# 消息摘要所需的密钥数量(默认8个)
self.msg_key_count = 8
# 校验和密钥数量
self.cs_key_count = 0
# 总密钥数量 = 消息密钥 + 校验和密钥
self.key_count = self.msg_key_count + self.cs_key_count
self.hashfunction = hashfunction
self.digestsize = digestsize
self.pubkey = pubkey @staticmethod
def number_to_base(num: int, base: int) -> List[int]:
if num == 0:
return [0] # 如果数字是 0,直接返回 0 digits = [] # 存储转换后的数字位
while num:
digits.append(int(num % base)) # 获取当前数字在目标进制下的个位,并添加到结果列表
num //= base # 对数字进行整除,处理下一位 return digits[::-1] # 返回按顺序排列的结果 def _chain(self, value: bytes, startidx: int, endidx: int) -> bytes:
for i in range(startidx, endidx):
value = self.hashfunction(value) # 每次迭代对当前哈希值进行哈希操作 return value def get_signature_base_message(self, msghash: bytes) -> List[int]:
# 将消息哈希从字节转换为整数
msgnum = int.from_bytes(msghash, "big") # 将消息的数字表示转换为特定进制下的比特组表示
msg_to_sign = self.number_to_base(msgnum, self.w) # 校验消息比特组的数量是否符合预期
if len(msg_to_sign) > self.msg_key_count:
err = (
"The fingerprint of the message could not be split into the"
+ " expected amount of bitgroups. This is most likely "
+ "because the digestsize specified does not match to the "
+ " real digestsize of the specified hashfunction Excepted:"
+ " {} bitgroups\nGot: {} bitgroups"
)
raise IndexError(err.format(self.msg_key_count, len(msg_to_sign))) return msg_to_sign def get_pubkey_from_signature(
self, digest: bytes, signature: List[bytes]
) -> List[bytes]:
msg_to_verify = self.get_signature_base_message(digest) result = []
for idx, val in enumerate(msg_to_verify):
sig_part = signature[idx]
chained_val = self._chain(sig_part, val, self.w - 1)
result.append(chained_val)
return result def verify(self, digest: bytes, signature: List[bytes]) -> bool:
pubkey = self.get_pubkey_from_signature(digest, signature)
return True if pubkey == self.pubkey else False if __name__ == "__main__":
pubkey_hex = "5057432973dc856a7a00272d83ea1c14de52b5eb3ba8b70b373db8204eb2f902450e38dbade5e9b8c2c3f8258edc4b7e8101e94ac86e4b3cba92ddf3d5de2a2b454c067a995060d1664669b45974b15b3423cec342024fe9ccd4936670ec3abaae4f6b97279bd8eb26463a8cb3112e6dcbf6301e4142b9cdc4adfb644c7b114af4f0cf8f80e22c3975ba477dc4769c3ef67ffdf2090735d81d07bc2e6235af1ee41ef332215422d31208c2bc2163d6690bd32f4926b2858ca41c12eec88c0a300571901a3f674288e4a623220fb6b70e558d9819d2f23da6d897278f4056c346d7f729f5f70805ad4e5bd25cfa502c0625ac02185e014cf36db4ebcdb3ed1a38"
pubkey_list_bytes = hex_to_32byte_chunks(pubkey_hex)
wots = WOTSPLUS(pubkey = pubkey_list_bytes)
digest_hex = "84ffb82e"
signature_hex = "25d5a0e650d683506bfe9d2eca6a3a99b547a4b99398622f6666ce10131e971b6bd36841c9074fe9b4de2900ebe3fadb3202a173be486da6cf8f3d8c699c95c3454c067a995060d1664669b45974b15b3423cec342024fe9ccd4936670ec3abaae4f6b97279bd8eb26463a8cb3112e6dcbf6301e4142b9cdc4adfb644c7b114a4966398a789b56bdb09ea195925e7e8cde372305d244604c48db08f08a6e8a38951030deb25a7aaf1c07152a302ebc07d5d0893b5e9a5953f3b8500179d138b9aa90c0aaacea0c23d22a25a86c0b747c561b480175b548fcb1f4ad1153413bc74d9c049d43ffe18ceee31e5be8bdb9968103ef32fb4054a4a23c400bbfe0d89f"
digest_bytes = bytes.fromhex(digest_hex)
signature = hex_to_32byte_chunks(signature_hex)
valid = wots.verify(digest_bytes,signature)
print(valid)

analysis

  • 针对于这道题而言,从wots.verify一步步跟进发现这道题,给出公钥,输入信息摘要以及其针对于该信息的签名值。
  • 跟进get_pubkey_from_signature发现该签名的流程是对sig_part进行x次的哈希,返回的结果作为签名值。这里考虑到哈希函数的不可逆性,所以我们就要把思路转换到恶意伪造签名上而非进行由公钥进行求解私钥再进行计算。
  • 随之发现其中_chain操作的次数与所传入的参数信息摘要的每一位有关,可以输出其中的参数msg_to_verify进行验证。
  • 因此,我们可以恶意伪造信息摘要为ffffffff,使其_chain操作次数均为0,那么签名值就是未经操作的公钥,伪造成功交互获取flag即可。
  • 看到languag3师傅的wp才知晓该签名验签程序为OTS签名,更能明白些许原理以及这样考虑的思路,更具指导性。

写在最后

  • 第一次参加熵密杯,题目质量很高,比起以往的CTF中的密码学方向的题目而言,课本上的知识得到了时间并且针对于加密算法的漏洞攻击而言,更加使用的签名和实用化的场景下进行的题目更加具有实际意义。
  • 本次比赛只解出了初始谜题3,第一次使用gmsll库,发现了该库简直是密码学应用的天然宝库。但是由于自己太菜了,没有享受到密码学的快乐,坐牢ed.
  • 因为比赛时全程都在看初始谜题,flag也因为实力太弱没能求解,同时对于Go的审计能力几乎为0,后续题解就不再记录,留个坑。还得xue...

2025熵密杯 -- 初始谜题 -- Reproducibility的更多相关文章

  1. NetBeans 8.0 连接远程服务器

    step: ① 新建项目 -- PHP -- 来自远程服务器的 PHP 应用程序 -- 下一步 ② 名称和位置 项目名称:自己取: 源文件夹:通过 “浏览” 选择本地项目文件夹.注意文件夹内不能有 n ...

  2. 论文笔记(3):STC: A Simple to Complex Framework for Weakly-supervised Semantic Segmentation

    论文题目是STC,即Simple to Complex的一个框架,使用弱标签(image label)来解决密集估计(语义分割)问题. 2014年末以来,半监督的语义分割层出不穷,究其原因还是因为pi ...

  3. Python爬虫(四)——开封市58同城数据模型训练与检测

    前文参考: Python爬虫(一)——开封市58同城租房信息 Python爬虫(二)——对开封市58同城出租房数据进行分析 Python爬虫(三)——对豆瓣图书各模块评论数与评分图形化分析 数据的构建 ...

  4. Python爬虫(四)——豆瓣数据模型训练与检测

    前文参考: Python爬虫(一)——豆瓣下图书信息 Python爬虫(二)——豆瓣图书决策树构建 Python爬虫(三)——对豆瓣图书各模块评论数与评分图形化分析 数据的构建 在这张表中我们可以发现 ...

  5. 4 weekend110的hdfs&mapreduce测试 + hdfs的实现机制初始 + hdfs的shell操作 + 无密登陆配置

    Hdfs是根/目录,windows是每一个盘符, 1  从Linux里传一个到,hdfs里去 2  从hdfs里下一个到,linux里去 想从hdfs里,下载到linux, 涨知识,记住,hdfs是建 ...

  6. 2016 "Bird Cup" ICPC7th@ahstu--“波导杯”安徽科技学院第七届程序设计大赛

    "波导杯"安徽科技学院第七届程序设计大赛 Contest - 2016 "Bird Cup" ICPC7th@ahstu Start time:  2016-0 ...

  7. 2016 "Bird Cup" ICPC7th@ahstu--“波导杯”安徽科技学院第七届程序设计大赛

    "波导杯"安徽科技学院第七届程序设计大赛 原文章网页 Contest - 2016 "Bird Cup" ICPC7th@ahstu Start time:   ...

  8. 最大熵模型(Maximum Etropy)—— 熵,条件熵,联合熵,相对熵,互信息及其关系,最大熵模型。。

    引入1:随机变量函数的分布 给定X的概率密度函数为fX(x), 若Y = aX, a是某正实数,求Y得概率密度函数fY(y). 解:令X的累积概率为FX(x), Y的累积概率为FY(y). 则 FY( ...

  9. 队爷的讲学计划 CH Round #59 - OrzCC杯NOIP模拟赛day1

    题目:http://ch.ezoj.tk/contest/CH%20Round%20%2359%20-%20OrzCC杯NOIP模拟赛day1/队爷的讲学计划 题解:刚开始理解题意理解了好半天,然后发 ...

  10. JVM上的随机数与熵池策略

    在apache-tomcat官方文档:如何让tomcat启动更快里面提到了一些启动时的优化项,其中一项是关于随机数生成时,采用的“熵源”(entropy source)的策略. 他提到tomcat7的 ...

随机推荐

  1. golang 接口按需获取资源

    场景 爬虫业务场景,我们需要调用三方接口获取代理ip地址,每个ip地址可以使用的时间有限和价格的,本着不浪费资源,我们在这里做一层封装. 当有其他业务调用我们接口的时候,会拉起定时任务,这个定时任务的 ...

  2. Everyone's Favorite Linear, Direct Access, Homogeneous Data Structure: The Array(英翻中)

    Arrays are one of the simplest and most widely used data structures in computer programs. Arrays in ...

  3. Feign Client 超时时间配置

      在Spring Boot微服务架构中,大部分公司都是利用Open Feign进行服务间的调用,而在业务场景比较简单的时候,使用默认配置是不会遇到多大问题的.但是如果业务比较复杂,服务要进行比较繁杂 ...

  4. Svelte 5 在跨平台 AI 阅读助手中的实践:轻量化前端架构的极致性能优化

    一.引言:为什么选择 Svelte 5 来构建 Saga Reader 的前端? 作为一个典型的前端开发者,去年在为公司调研Rust前端工具链.LLM应用开发技术体系的时候,对这类技术领域产生了浓厚的 ...

  5. Golang的格式化输出

    一.格式化说明符 通用占位符: %v 值的默认格式表示 %+v 类似%v,但输出结构体时会添加字段名 %#v 值的Go语法表示 %T 打印值的类型 %% 输出百分号 布尔型占位符: %t 接收bool ...

  6. USB Gadget设备软插拔异常的处理方法

    技术背景 我们的板子作为 USB Gadget 设备通过 USB 线接入 USB 主机使用,我们的板子被主机识别为一个 Compsite Device,这个 Compsite Device 是由我们板 ...

  7. UFT 获取linux log

  8. Blazor学习之旅:文章目录索引

    1 Blazor学习路径 微软学习社区(Microsoft Learn)早在两三年前就推出了一门学习路径<使用Blazor构建Web应用程序>,我们可以学到如何通过Blazor Web应用 ...

  9. C#  自定义特性属性 限制自定义字段 数据

    不知道怎么表达这个东西,先记录一下吧,如果你们有好的想法可以联系我,共同进步 /// <summary>        /// 自定义属性帮助类        /// </summa ...

  10. C# 有错误日志返回的POST 请求 WebException

    /// <summary>        /// POST请求        /// </summary>        /// <param name="ur ...