def verify(self): x, y = DEREncoder.decode_signature(base64.b64decode(self.sender)) public_key = Point(x, y, Transaction._curve) r, s = DEREncoder.decode_signature(base64.b64decode(self.signature)) return ecdsa.verify((r, s), self.data, public_key, curve=Transaction._curve)
def test_encode_decode_all_curves(self): for curve in CURVES: d = randint(1, curve.q) Q = d * curve.G r, s = sign("sign me", d, curve=curve) encoded = DEREncoder.encode_signature(r, s) decoded_r, decoded_s = DEREncoder.decode_signature(encoded) self.assertEqual(decoded_r, r) self.assertEqual(decoded_s, s)
def handle_ecdh(self, body): self.ecdh_blob = body key, signature = body[:0x90], body[0x90:] x = key[0x8:0x8+0x20] y = key[0x4c:0x4c+0x20] x, y = [int(hexlify(i[::-1]), 0x10) for i in [x, y]] if not P256.is_point_on_curve( (x, y) ): raise Exception('Point is not on the curve') self.ecdh_q = Point(x, y, P256) self.trace('ECDH params:') self.trace('x=0x%x' % x) self.trace('y=0x%x' % y) l, signature = signature[:4], signature[4:] l, = unpack('<L', l) signature, zeroes = signature[:l], signature[l:] if zeroes != b'\0'*len(zeroes): raise Exception('Zeroes expected') # The following pub key is hardcoded for each fw revision in the synaWudfBioUsb.dll. # Corresponding private key should only be known to a genuine Synaptic device. fwpub=Point( 0xf727653b4e16ce0665a6894d7f3a30d7d0a0be310d1292a743671fdf69f6a8d3, 0xa85538f8b6bec50d6eef8bd5f4d07a886243c58b2393948df761a84721a6ca94, P256) signature=DEREncoder().decode_signature(signature) if not verify(signature, key, fwpub): raise Exception('Untrusted device')
def export_recovery_seed(self, allow_algs): for alg in allow_algs: if alg == 0: if self._recovery_seed_pri_key is None: self._initialize_recovery_seed() S = fastecdsa.keys.get_public_key(self._recovery_seed_pri_key, P256) S_enc = encode_pub(S) signed_data = struct.pack('>B16s65s', alg, self._aaguid, S_enc) sig = DEREncoder.encode_signature( *ecdsa.sign( signed_data, self._attestation_key, hashfunc=hashlib.sha256 ) ) payload = { 1: alg, 2: [], 3: self._aaguid, 4: sig, -1: S_enc, } return cbor.encode(payload) raise UnknownKeyAgreementScheme(allow_algs)
def convert_der_sig(signature, as_hex=True): """ Extract content from DER encoded string: Convert DER encoded signature to signature string. :param signature: DER signature :type signature: bytes :param as_hex: Output as hexstring :type as_hex: bool :return bytes, str: Signature """ if not signature: return "" if USE_FASTECDSA: r, s = DEREncoder.decode_signature(bytes(signature)) else: sg, junk = ecdsa.der.remove_sequence(signature) if junk != b'': raise EncodingError("Junk found in encoding sequence %s" % junk) r, sg = ecdsa.der.remove_integer(sg) s, sg = ecdsa.der.remove_integer(sg) sig = '%064x%064x' % (r, s) if as_hex: return sig else: return binascii.unhexlify(sig)
def verifyVote(self, privateKey): if (self.signature == "" or len(self.signature) == 0): return False r, s = ecdsa.sign(self.voteHash, privateKey, curve=curve.secp256k1, hashfunc=hl.sha256) decoded_r, decoded_s = DEREncoder.decode_signature(self.signature) # Se os parâmetros são diferentes, a assinatura é inválida if (r != decoded_r or s != decoded_s): return False # Verifica a assinatura valid = ecdsa.verify((r, s), self.voteHash, self.voter, curve=curve.secp256k1, hashfunc=hl.sha256) if (not valid): return False return True
def sign(signing_key: SigningKey, message_digest_bytes: bytes) -> bytes: r, s = ecdsa.sign( message_digest_bytes.hex(), signing_key, curve=curve.secp256k1, prehashed=True, ) return DEREncoder.encode_signature(r, s)
def signVote(self, privateKey): # Assina a cédula r, s = ecdsa.sign(self.voteHash, privateKey, curve=curve.secp256k1, hashfunc=hl.sha256) # Transforma a assinatura para DER self.signature = DEREncoder.encode_signature(r, s)
def make_cert(client_public): msg = (pack('<LL', 0x17, 0x20) + unhexlify('%064x' % client_public.x)[::-1] + (b'\0' * 0x24) + unhexlify('%064x' % client_public.y)[::-1] + (b'\0' * 0x4c)) s = sign(msg, hs_key()) s = DEREncoder().encode_signature(s[0], s[1]) s = pack('<L', len(s)) + s msg = msg + s msg += b'\0' * (444 - len(msg)) # FIXME not sure this math is right return msg
def authenticator_make_credential(self, args_cbor): args = cbor.decode(args_cbor) clientDataJSON_hash = args[0x01] rp_id = args[0x02]['id'] extension_inputs = args[0x06] rp_id_hash = sha256(rp_id.encode('utf-8')) flags = 0b11000001 # ED, AT, UP sign_count = 0 credential_id = secrets.token_bytes(32) (credential_private_key, credential_public_key) = fastecdsa.keys.gen_keypair(P256) self._credentials[credential_id] = credential_private_key attested_cred_data = pack_attested_credential_data( self._aaguid, credential_id, credential_public_key, ) authData_without_extensions = struct.pack( f'>32sBL{len(attested_cred_data)}s', rp_id_hash, flags, sign_count, attested_cred_data, ) extensions = {} if "recovery" in extension_inputs: extensions["recovery"] = self.process_recovery_extension( rp_id, authData_without_extensions, clientDataJSON_hash, extension_inputs["recovery"], ) authData = authData_without_extensions + cbor.encode(extensions) attStmt = { 0x01: 'packed', 0x02: authData, 0x03: { 'alg': -7, 'sig': DEREncoder.encode_signature( *ecdsa.sign( authData + clientDataJSON_hash, credential_private_key, hashfunc=hashlib.sha256 ) ), }, } return cbor.encode(attStmt)
def build_authorization_header(self): """Build authorization headers.""" if len(self.required_authorization_headers) > 0: self.authorization_parameters['headers'] = '{}'.format(' '.join([ header.lower() for header in self.required_authorization_headers ])) self.build_signing_string() signing_bytestring = self.signing_string.encode(self.encoding) if self.signing_algorithm == 'RSA': signer = RSA.import_key(self.private_key_string) if self.hashing_algorithm == 'SHA256': hash_obj = SHA256.new(signing_bytestring) elif self.hashing_algorithm == 'SHA512': hash_obj = SHA512.new(signing_bytestring) else: raise Exception("Invalid key type") signature = pkcs1_15.new(signer).sign(hash_obj) else: private_key, public_key = PEMEncoder.decode_private_key( self.private_key_string) if self.hashing_algorithm == 'SHA256': hash_function = hashlib.sha256 elif self.hashing_algorithm == 'SHA512': hash_function = hashlib.sha512 else: raise Exception("Invalid key type") if self.signing_algorithm.lower() == 'p256': r, s = ecdsa.sign(signing_bytestring, private_key, curve=curve.P256, hashfunc=hash_function) else: raise Exception("Invalid key type") signature = DEREncoder.encode_signature(r, s) base64_signature = base64.b64encode(signature).decode(self.encoding) self.authorization_parameters['signature'] = '{}'.format( base64_signature) authorization_rows = [] for key, value in self.authorization_parameters.items(): if isinstance(value, str): authorization_rows.append('{}="{}"'.format(key, value)) elif isinstance(value, int) or isinstance(value, float): authorization_rows.append('{}={}'.format(key, value)) elif isinstance(value, bool): if value is True: authorization_rows.append('{}=true') else: authorization_rows.append('{}=false') authorization_header = 'Signature {}'.format( ','.join(authorization_rows)) self.headers['Authorization'] = authorization_header
def verify( verifying_key: VerifyingKey, message_digest_bytes: bytes, signature_bytes: bytes, ) -> bool: r, s = DEREncoder.decode_signature(signature_bytes) return ecdsa.verify( (r, s), message_digest_bytes, verifying_key, curve=curve.secp256k1, hashfunc=IdentityHash, )
def sign(signing_key: SigningKey, message_digest_bytes: bytes) -> bytes: r, s = ecdsa.sign( message_digest_bytes, signing_key, curve=curve.secp256k1, prehashed=True, ) # Both (r, s) and (r, -s mod q = q - s) are valid, canonical signatures. # (r, s) is fully canonical only when s <= q - s. s_inverse = curve.secp256k1.q - s if s > s_inverse: s = s_inverse return DEREncoder.encode_signature(r, s)
def der_encode_sig(r, s): """ Create DER encoded signature string with signature r and s value :param r: r value of signature :type r: int :param s: s value of signature :type s: int :return bytes: """ if USE_FASTECDSA: return DEREncoder.encode_signature(r, s) else: ecdsa.der.sigencode_der(r, s)
def _test_runner(self, tests, curve, hashfunc): for test_group in tests: keybytes = unhexlify(test_group["key"]["uncompressed"]) public_key = SEC1Encoder.decode_public_key(keybytes, curve) for test in test_group["tests"]: try: message = unhexlify(test["msg"]) sigbytes = unhexlify(test["sig"]) signature = DEREncoder.decode_signature(sigbytes) expected = test["result"] == "valid" result = verify(signature, message, public_key, curve, hashfunc) self.assertEqual(result, expected, test) except: self.assertFalse(test["result"] == "valid", test)
def sign(message: bytes, private_key: PrivateKey) -> Signature: digest = hashes.sha512half(message) signing_key = int.from_bytes(private_key, byteorder='big') r, s = ecdsa.sign( digest.hex(), signing_key, curve=curve.secp256k1, prehashed=True, ) # Both (r, s) and (r, -s mod G = G - s) are valid, canonical signatures. # (r, s) is fully canonical only when s <= G - s. s_inverse = GROUP_ORDER - s if s > s_inverse: s = s_inverse signature = DEREncoder.encode_signature(r, s) return t.cast(Signature, signature)
def verify_correctness(): clientDataJSON_hash = os.urandom(32) primary_authnr = Authenticator() backup_authnr = Authenticator() # Create a credential with the primary authenticator basic_make_credential(primary_authnr, clientDataJSON_hash) allowList = [{ 'type': 'public-key', 'id': i } for i in primary_authnr._credentials.keys()] # Transfer recovery seed from backup authenticator to primary primary_authnr.import_recovery_seed(backup_authnr.export_recovery_seed([0 ])) # Generate a recovery credential with the primary authenticator recovery_creds = ctap2.AuthenticatorData( cbor.decode( generate_backups_get_assertion( primary_authnr, clientDataJSON_hash, allowList))[2]).extensions['recovery']['creds'] recovery_pubkey = cose_key_to_point( ctap2.AttestedCredentialData(recovery_creds[0]).public_key) recovery_allowCredentials = [{ 'id': ctap2.AttestedCredentialData(cred).credential_id, 'type': 'public-key' } for cred in recovery_creds] # Perform recovery registration with backup authenticator attObj_bytes = recovery_make_credential(backup_authnr, clientDataJSON_hash, recovery_allowCredentials) att_obj = ctap2.AttestationObject(attObj_bytes) # Verify that backup authenticator returns the correct recovery credential ID recovery_cred_id = att_obj.auth_data.extensions['recovery']['credId'] assert recovery_cred_id == ctap2.AttestedCredentialData( recovery_creds[0]).credential_id # Verify that backup authenticator returns a valid recovery signature auth_data_without_extensions = att_obj.auth_data[:37 + len(att_obj.auth_data. credential_data)] recovery_sig = att_obj.auth_data.extensions['recovery']['sig'] assert ecdsa.verify(DEREncoder.decode_signature(recovery_sig), auth_data_without_extensions + clientDataJSON_hash, recovery_pubkey)
def authenticator_get_assertion(self, args_cbor): args = cbor.decode(args_cbor) rp_id = args[0x01] clientDataJSON_hash = args[0x02] extension_inputs = args[0x04] rp_id_hash = sha256(rp_id.encode('utf-8')) flags = 0b10000001 # ED, UP sign_count = 0 extensions = {} authData_without_extensions = struct.pack( f'>32sBL', rp_id_hash, flags, sign_count, ) if "recovery" in extension_inputs: extensions["recovery"] = self.process_recovery_extension( rp_id, authData_without_extensions, clientDataJSON_hash, extension_inputs["recovery"] ) authData = authData_without_extensions + cbor.encode(extensions) sig = None for cred_descriptor in args[0x03]: if cred_descriptor['id'] in self._credentials: sig = DEREncoder.encode_signature( *ecdsa.sign( authData + clientDataJSON_hash, self._credentials[cred_descriptor['id']], hashfunc=hashlib.sha256 ) ) break if sig is None: raise NoCredentialAvailable() return cbor.encode({ 0x01: cred_descriptor, 0x02: authData, 0x03: sig, })
def der_encode_sig(r, s): """ Create DER encoded signature string with signature r and s value. :param r: r value of signature :type r: int :param s: s value of signature :type s: int :return bytes: """ if USE_FASTECDSA: return DEREncoder.encode_signature(r, s) else: rb = ecdsa.der.encode_integer(r) sb = ecdsa.der.encode_integer(s) return ecdsa.der.encode_sequence(rb, sb)
def __validate_fastecdsa_signature(self, verification_string, signature, charset): """Return True if signature is valid, using FastEDSA algorithms.""" ecdsa_public_key = PEMEncoder.decode_public_key( self.inflated_public_key) # signature_length = len(signature) r, s = DEREncoder.decode_signature(signature) # r_bytes, s_bytes = signature[:signature_length // 2], signature[signature_length // 2:signature_length] # r, s = int.from_bytes(r_bytes, 'big', signed=False), int.from_bytes(s_bytes, 'big', signed=False) signing_algorithm = self.signing_algorithm.upper() if ('SHA-' in signing_algorithm): signing_algorithm = signing_algorithm.replace('SHA-', 'SHA') if ('P-' in signing_algorithm): signing_algorithm = signing_algorithm.replace('P-', 'P') if signing_algorithm == self.SIGNING_ALGORITHM_ECDSA_P256: curve_algorithm = curve.P256 elif signing_algorithm == self.SIGNING_ALGORITHM_ECDSA_P384: curve_algorithm = curve.P384 elif signing_algorithm == self.SIGNING_ALGORITHM_ECDSA_P521: curve_algorithm = curve.P2521 elif signing_algorithm == self.SIGNING_ALGORITHM_ECDSA_CURVE25519: curve_algorithm = curve.W25519 else: raise UnsupportedAlgorithmException(self.signing_algorithm) hashing_algorithm = self.hashing_algorithm.upper().replace('-', '') if hashing_algorithm == self.HASHING_ALGORITHM_SHA256: hash_function = hashlib.sha256 elif hashing_algorithm == self.HASHING_ALGORITHM_SHA512: hash_function = hashlib.sha512 else: raise UnsupportedAlgorithmException(self.hashing_algorithm) is_valid = False try: ecdsa.verify((r, s), signature, ecdsa_public_key, curve=curve_algorithm, hashfunc=hash_function) is_valid = True except ecdsa.EcdsaError: is_valid = False return is_valid
def _generate_recovery_signature( self, rp_id, authData_without_extensions, clientDataHash, allow_credentials, ): if self._recovery_seed_pri_key is None: return InvalidState() for cred in allow_credentials: cred_id = cred['id'] alg = cred_id[0] if alg == 0: try: cred_pri = self._derive_private_key( self._recovery_seed_pri_key, cred_id, rp_id) sig = DEREncoder.encode_signature( *ecdsa.sign( authData_without_extensions + clientDataHash, cred_pri, hashfunc=hashlib.sha256 ) ) except RpIdMismatch: continue else: continue extension_output = { 'action': 'recover', 'credId': cred_id, 'sig': sig, 'state': self._state, } return extension_output raise NoCredentialAvailable()
def makecredential_process_recovery_extension( self, authData, clientDataHash, extension_output, ): action = extension_output['action'] if action == 'state': pass elif action == 'recover': authData_without_extensions = authData pubkey_cose = self._recovery_credentials[ extension_output['credId'] ].public_key pubkey = cose_key_to_point(pubkey_cose) assert ecdsa.verify( DEREncoder.decode_signature(extension_output['sig']), authData_without_extensions + clientDataHash, pubkey, hashfunc=hashlib.sha256 ) else: raise UnknownAction()
def test_encode_signature(self): self.assertEqual( DEREncoder.encode_signature(r=1, s=2), b"\x30" # SEQUENCE b"\x06" # Length of Sequence b"\x02" # INTEGER b"\x01" # Length of r b"\x01" # r b"\x02" # INTEGER b"\x01" # Length of s b"\x02", # s ) # Check that we add a zero byte when the number's highest bit is set self.assertEqual(DEREncoder.encode_signature(r=128, s=128), b"0\x08\x02\x02\x00\x80\x02\x02\x00\x80") # Check a value on a standard curve like secp256k1 works # see https://github.com/btccom/secp256k1-go/blob/master/secp256k1/sign_vectors.yaml secp256k1_vectors = [ ( 0x31a84594060e103f5a63eb742bd46cf5f5900d8406e2726dedfc61c7cf43ebad, unhexlify( "9e5755ec2f328cc8635a55415d0e9a09c2b6f2c9b0343c945fbbfe08247a4cbe" ), unhexlify( "30440220132382ca59240c2e14ee7ff61d90fc63276325f4cbe8169fc53ade4a407c2fc802204d86fbe3bde69" "75dd5a91fdc95ad6544dcdf0dab206f02224ce7e2b151bd82ab"), ), ( 0x7177f0d04c79fa0b8c91fe90c1cf1d44772d1fba6e5eb9b281a22cd3aafb51fe, unhexlify( "2d46a712699bae19a634563d74d04cc2da497b841456da270dccb75ac2f7c4e7" ), unhexlify( "3045022100d80cf7abc9ab601373780cee3733d2cb5ff69ba1452ec2d2a058adf9645c13be0220011d1213b7d" "152f72fd8759b45276ba32d9c909602e5ec89550baf3aaa8ed950"), ), ( 0x989e500d6b1397f2c5dcdf43c58ac2f14df753eb6089654e07ff946b3f84f3d5, unhexlify( "c94f4ec84be928017cbbb447d2ab5b5d4d69e5e5fd03da7eae4378a1b1c9c402" ), unhexlify( "3045022100d0f5b740cbe3ee5b098d3c5afdefa61bb0797cb4e7b596afbd38174e1c653bb602200329e9f1a09" "632de477664814791ac31544e04715db68f4b02657ba35863e711"), ), ( 0x39dfc615f2b718397f6903b0c46c47c5687e97d3d2a5e1f2b200f459f7b1219b, unhexlify( "dfeb2092955572ce0695aa038f58df5499949e18f58785553c3e83343cd5eb93" ), unhexlify( "30440220692c01edf8aeab271df3ed4e8d57a170f014f8f9d65031aac28b5e1840acfb5602205075f9d1fdbf5" "079ee052e5f3572d518b3594ef49582899ec44d065f71a55192"), ), ] for private_key, digest, expected in secp256k1_vectors: r, s = sign(digest, private_key, curve=secp256k1, prehashed=True) encoded = DEREncoder.encode_signature(r, s) self.assertEqual(encoded, expected)
def test_decode_signature(self): with self.assertRaises(InvalidDerSignature): DEREncoder.decode_signature(b"") # length too short with self.assertRaises(InvalidDerSignature): DEREncoder.decode_signature( b"\x31\x06\x02\x01\x01\x02\x01\x02") # invalid SEQUENCE marker with self.assertRaises(InvalidDerSignature): DEREncoder.decode_signature(b"\x30\x07\x02\x01\x01\x02\x01\x02" ) # invalid length (too short) with self.assertRaises(InvalidDerSignature): DEREncoder.decode_signature(b"\x30\x05\x02\x01\x01\x02\x01\x02" ) # invalid length (too long) with self.assertRaises(InvalidDerSignature): DEREncoder.decode_signature( b"\x30\x06\x02\x03\x01\x02\x01\x02") # invalid length of r with self.assertRaises(InvalidDerSignature): DEREncoder.decode_signature( b"\x30\x06\x02\x01\x01\x02\x03\x02") # invalid length of s with self.assertRaises(InvalidDerSignature): DEREncoder.decode_signature(b"\x30\x06\x03\x01\x01\x02\x01\x02" ) # invalid INTEGER marker for r with self.assertRaises(InvalidDerSignature): DEREncoder.decode_signature( b"\x30\x06\x02\x00\x02\x01\x02") # length of r is 0 with self.assertRaises(InvalidDerSignature): DEREncoder.decode_signature( b"\x30\x06\x02\x01\x81\x02\x01\x02") # value of r is negative with self.assertRaises(InvalidDerSignature): DEREncoder.decode_signature(b"\x30\x07\x02\x02\x00\x01\x02\x01\x02" ) # value of r starts with a zero byte with self.assertRaises(InvalidDerSignature): DEREncoder.decode_signature(b"\x30\x06\x02\x01\x01\x03\x01\x02" ) # invalid INTEGER marker for s with self.assertRaises(InvalidDerSignature): DEREncoder.decode_signature( b"\x30\x06\x02\x01\x01\x02\x00") # value of s is 0 with self.assertRaises(InvalidDerSignature): DEREncoder.decode_signature( b"\x30\x06\x02\x01\x01\x02\x01\x81") # value of s is negative with self.assertRaises(InvalidDerSignature): DEREncoder.decode_signature(b"\x30\x07\x02\x01\x01\x02\x02\x00\x02" ) # value of s starts with a zero byte self.assertEqual( DEREncoder.decode_signature(b"\x30\x06\x02\x01\x01\x02\x01\x02"), (1, 2)) self.assertEqual( DEREncoder.decode_signature( b"0\x08\x02\x02\x00\x80\x02\x02\x00\x80"), (128, 128)) # verify zero bytes self.assertEqual( DEREncoder.decode_signature( b"0\x08\x02\x02\x03\xE8\x02\x02\x03\xE8"), (1000, 1000)) # verify byte order
nulls, p = p[:0x24], p[0x24:] if nulls != b'\0' * len(nulls): raise Exception('Nulls expected') y, p = p[:0x20], p[0x20:] y = int(hexlify(y[::-1]).decode(), 16) print('block 5, y=%x' % y) if not P256.is_point_on_curve((x, y)): raise Exception('pub key point is not on curve') nulls, p = p[:0x4c], p[0x4c:] if nulls != b'\0' * len(nulls): raise Exception('Nulls expected') ssz, p = p[:4], p[4:] ssz, = unpack('<L', ssz) signature, nulls = p[:ssz], p[ssz:] if nulls != b'\0' * len(nulls): raise Exception('Nulls expected') signature = DEREncoder().decode_signature(signature) msg = (pack('<LL', 0x17, 0x20) + unhexlify('%064x' % x)[::-1] + (b'\0' * 0x24) + unhexlify('%064x' % y)[::-1] + (b'\0' * 0x4c)) pub = get_public_key(hs_key(), P256) if not verify(signature, msg, pub): raise Exception('Signature validation failed')
def address(self): return base64.b64encode(DEREncoder.encode_signature( self.public_key.x, self.public_key.y))
def make_cert_verify(self): buf=self.handshake_hash.copy().digest() s=sign(hexlify(buf).decode(), self.priv_key, prehashed=True) b=DEREncoder().encode_signature(s[0], s[1]) return self.with_neg_hdr(0x0f, b)
def sign(self, message_bytes): r, s = ecdsa.sign(message_bytes, self.private_key, curve=self.public_key.curve) return DEREncoder.encode_signature(r, s).hex()
def verify(signature, message_bytes: str, public_key: Point): r, s = DEREncoder.decode_signature(bytes.fromhex(signature)) return ecdsa.verify((r, s), message_bytes, public_key, curve=public_key.curve)
def _sign_transaction(self, transaction): r, s = ecdsa.sign(transaction.data, self.private_key, curve=Wallet._curve) signature = base64.b64encode(DEREncoder.encode_signature(r, s)) transaction.add_signature(signature)