def test_ec_pubkey_parse(self): for flags in self.context_flags: # Create context ctx = secpy256k1.context_create(flags) # Parse variable length public key from bytes secp256k1_pubkey_tuple = secpy256k1.ec_pubkey_parse( ctx, self.pubkey) # First tuple entry returns a 1 for a fully valid public key self.assertEqual(secp256k1_pubkey_tuple[0], 1) # Second tuple entry returns a pointer to a secp256k1_pubkey self.assertEqual(secpy256k1.ffi.typeof(secp256k1_pubkey_tuple[1]), secpy256k1.ffi.typeof('secp256k1_pubkey *')) # Errors if invalid context flag with self.assertRaises(TypeError) as err: secpy256k1.ec_pubkey_parse(0, self.pubkey) self.assertIn( 'Invalid context. Must be secp256k1_context_struct pointer.', str(err.exception)) # Errors if invalid seralized public key with self.assertRaises(ValueError) as err: secpy256k1.ec_pubkey_parse(ctx, 0) self.assertIn('Invalid pubkey. Must be 33- or 65-bytes.', str(err.exception))
def verify_hash(pubkey: bytes, sig: bytes, digest: bytes) -> bool: ''' Verifies a signature on a specific hash NB: ECDSA is NOT SECURE unless the verifier calculates the hash Args: pubkey (bytes): the public key in compressed or uncompressed form sig (bytes): the der encoded signature digest (bytes): the 32 byte message digest Returns: (bool): True if valid signature, false otherwise ''' if len(digest) != 32: raise ValueError('Digest must be 32 bytes') ctx = get_verify_context() pubkey_tuple = secpy256k1.ec_pubkey_parse(ctx, pubkey) if pubkey_tuple[0] != 1: raise Exception('unknown exception -- pubkey parse failed') sig_tuple = secpy256k1.ecdsa_signature_parse_der(ctx, sig) if sig_tuple[0] != 1: raise Exception('unknown exception -- sig parse failed') res = secpy256k1.ecdsa_verify(ctx, sig_tuple[1], digest, pubkey_tuple[1]) return True if res == 1 else False
def tweak_pubkey_mul(pubkey: bytes, tweak: bytes, compressed: bool = True) -> bytes: ''' Tweaks a pubkey by multiplying by a 32 byte tweak Args: pubkey (bytes): 32 byte pubkey tweak (bytes): 32 byte tweak Returns: (bytes): 32 byte tweaked pubkey ''' ctx = get_verify_context() if len(tweak) != 32: raise ValueError('tweak must be 32 bytes') pubkey_tuple = secpy256k1.ec_pubkey_parse(ctx, pubkey) if pubkey_tuple[0] != 1: raise Exception('unknown exception -- pubkey parse failed') tweaked_tuple = secpy256k1.ec_pubkey_tweak_mul(ctx, pubkey_tuple[1], tweak) if tweaked_tuple[0] != 1: raise Exception('unknown exception -- pubkey tweak mul failed') flag = (secpy256k1.lib.SECP256K1_EC_COMPRESSED if compressed else secpy256k1.lib.SECP256K1_EC_UNCOMPRESSED) pubkey_ser_tuple = secpy256k1.ec_pubkey_serialize(ctx, tweaked_tuple[1], flag) if pubkey_ser_tuple[0] != 1: raise Exception('unknown exception -- pubkey ser failed') return bytes(secpy256k1.ffi.buffer(pubkey_ser_tuple[1]))
def test_ecdsa_verify(self): verify_context = secpy256k1.context_create( secpy256k1.lib.SECP256K1_CONTEXT_VERIFY) parsed_sig_tuple = secpy256k1.ecdsa_signature_parse_compact( verify_context, self.compact_sig) secp256k1_pubkey_tuple = secpy256k1.ec_pubkey_parse( verify_context, self.pubkey) verify_res = secpy256k1.ecdsa_verify(verify_context, parsed_sig_tuple[1], self.msg, secp256k1_pubkey_tuple[1]) self.assertEqual(verify_res, 1)
def test_ec_privkey_tweak_add(self): # Create context secp256k1_ctx = secpy256k1.context_create( secpy256k1.lib.SECP256K1_CONTEXT_VERIFY) # Create COMPRESSED secp256k1_pubkey object to add tweak secp256k1_pubkey_tuple = secpy256k1.ec_pubkey_parse( secp256k1_ctx, self.pubkey) secp256k1_pubkey = secp256k1_pubkey_tuple[1] # Tweaked secp256k1_pubkey secp256k1_pubkey_tweak_tuple = secpy256k1.ec_pubkey_tweak_add( secp256k1_ctx, secp256k1_pubkey, self.tweak) # First tuple entry returns a 1 for a fully valid public key self.assertEqual(secp256k1_pubkey_tweak_tuple[0], 1) # Second tuple entry returns a tweaked secp256k1_pubkey pointer type self.assertEqual( secpy256k1.ffi.typeof(secp256k1_pubkey_tweak_tuple[1]), secpy256k1.ffi.typeof('secp256k1_pubkey *')) # Errors if invalid context with self.assertRaises(TypeError) as err: secpy256k1.ec_pubkey_tweak_add(0, self.pubkey, self.tweak) self.assertIn( 'Invalid context. Must be secp256k1_context_struct pointer.', str(err.exception)) # Errors if invalid pubkey with self.assertRaises(TypeError) as err: secpy256k1.ec_pubkey_tweak_add(secp256k1_ctx, self.pubkey, self.tweak) self.assertIn('Invalid pubkey. Must be secp256k1_pubkey pointer.', str(err.exception)) # Errors if invalid tweak with self.assertRaises(ValueError) as err: secpy256k1.ec_pubkey_tweak_add(secp256k1_ctx, secp256k1_pubkey, bytes.fromhex('00000001')) self.assertIn('Invalid tweak. Must be 32-bytes.', str(err.exception))
def compress_pubkey(pubkey: bytes) -> bytes: ''' Converts an uncompressed pubkey to a compressed pubkey Args: pubkey (bytes): the 65 byte uncompressed key Returns: (bytes): the compressed (33 byte) pubkey ''' ctx = get_verify_context() pubkey_tuple = secpy256k1.ec_pubkey_parse(ctx, pubkey) if pubkey_tuple[0] != 1: raise Exception('unknown exception -- pubkey parse failed') pubkey_ser_tuple = secpy256k1.ec_pubkey_serialize( ctx, pubkey_tuple[1], secpy256k1.lib.SECP256K1_EC_COMPRESSED) if pubkey_ser_tuple[0] != 1: raise Exception('unknown exception -- pubkey ser failed') return bytes(secpy256k1.ffi.buffer(pubkey_ser_tuple[1]))
def test_ec_pubkey_serialize(self): for flags in self.context_flags: # Create context secp256k1_ctx = secpy256k1.context_create(flags) # Create COMPRESSED secp256k1_pubkey object to serialize secp256k1_pubkey_tuple = secpy256k1.ec_pubkey_parse( secp256k1_ctx, self.pubkey) secp256k1_pubkey = secp256k1_pubkey_tuple[1] # Serialize COMPRESSED pubkey pubkey_ser_tuple = secpy256k1.ec_pubkey_serialize( secp256k1_ctx, secp256k1_pubkey, secpy256k1.lib.SECP256K1_EC_COMPRESSED) pubkey_int = pubkey_ser_tuple[0] pubkey_ser = pubkey_ser_tuple[1] pubkeylen = pubkey_ser_tuple[2] # First tuple entry always returns 1 self.assertEqual(pubkey_int, 1) # Second tuple entry returns type char[] pointer to COMPRESSED # public key byte array self.assertEqual(secpy256k1.ffi.typeof(pubkey_ser), secpy256k1.ffi.typeof('char[]')) self.assertEqual(secpy256k1.ffi.sizeof(pubkey_ser), 33) # Third tuple entry returns type size_t* pointer to public key size self.assertEqual(secpy256k1.ffi.typeof(pubkeylen), secpy256k1.ffi.typeof('size_t*')) # Errors if invalid context flag with self.assertRaises(TypeError) as err: secpy256k1.ec_pubkey_parse(0, self.pubkey) self.assertIn( 'Invalid context. Must be secp256k1_context_struct pointer.', str(err.exception)) # Errors if invalid seralized public key with self.assertRaises(ValueError) as err: secpy256k1.ec_pubkey_parse(secp256k1_ctx, 0) self.assertIn('Invalid pubkey. Must be 33- or 65-bytes.', str(err.exception)) for flags in self.context_flags: # Create context secp256k1_ctx = secpy256k1.context_create(flags) # Create UNCOMPRESSED secp256k1_pubkey object to serialize secp256k1_pubkey_tuple = secpy256k1.ec_pubkey_parse( secp256k1_ctx, self.uncomp_pubkey) secp256k1_pubkey = secp256k1_pubkey_tuple[1] # Serialize UNCOMPRESSED pubkey pubkey_ser_tuple = secpy256k1.ec_pubkey_serialize( secp256k1_ctx, secp256k1_pubkey, secpy256k1.lib.SECP256K1_EC_UNCOMPRESSED) pubkey_int = pubkey_ser_tuple[0] pubkey_ser = pubkey_ser_tuple[1] pubkeylen = pubkey_ser_tuple[2] # First tuple entry always returns 1 self.assertEqual(pubkey_int, 1) # Second tuple entry returns type char[] pointer to UNCOMPRESSED # public key byte array self.assertEqual(secpy256k1.ffi.typeof(pubkey_ser), secpy256k1.ffi.typeof('char[]')) self.assertEqual(secpy256k1.ffi.sizeof(pubkey_ser), 65) # Third tuple entry returns type size_t* pointer to public key size self.assertEqual(secpy256k1.ffi.typeof(pubkeylen), secpy256k1.ffi.typeof('size_t*')) # Errors if invalid context flag with self.assertRaises(TypeError) as err: secpy256k1.ec_pubkey_parse(0, self.pubkey) self.assertIn( 'Invalid context. Must be secp256k1_context_struct pointer.', str(err.exception)) # Errors if invalid seralized public key with self.assertRaises(ValueError) as err: secpy256k1.ec_pubkey_parse(secp256k1_ctx, 0) self.assertIn('Invalid pubkey. Must be 33- or 65-bytes.', str(err.exception))
'0290999dbbf43034bffb1dd53eac1eb4c33a4ea1c4f48ba585cfde3830840f0555' ) # noqa: E501 uncomp_pubkey = bytes.fromhex( '0490999dbbf43034bffb1dd53eac1eb4c33a4ea1c4f48ba585cfde3830840f05553a9d6d07e79ae2fbe0bc0b20c93e1f8e20d74b8a0a7028e32d9a6808b6c38df4' ) # noqa: E501 tweak = b'\x66' * 32 # noqa: E501 msg = bytes.fromhex('deadbeef' * 8) verify_context = secpy256k1.context_create(SECP256K1_CONTEXT_VERIFY) sign_context = secpy256k1.context_create(SECP256K1_CONTEXT_SIGN) # try cloning to make sure it doesn't error secpy256k1.context_clone(verify_context) # parse the pubkey to a secpy pubkey tuple secp256k1_pubkey_tuple = secpy256k1.ec_pubkey_parse(verify_context, pubkey) # serialize the pubkey output_tuple = secpy256k1.ec_pubkey_serialize(verify_context, secp256k1_pubkey_tuple[1], SECP256K1_EC_COMPRESSED) ser_pub = output_tuple[1] pubkey_ser = bytes(secpy256k1.ffi.buffer(ser_pub)) print('\n\npubkey_ser', pubkey_ser.hex()) # check pubkey tweak function tweaked_pubkey_tuple = secpy256k1.ec_pubkey_tweak_add( verify_context, secpy256k1.ec_pubkey_parse(verify_context, pubkey)[1], tweak) tweaked_pubkey = tweaked_pubkey_tuple[1]