2014-06-10 14:32:17 +02:00
#!/usr/bin/env python
#
# Electrum - lightweight Bitcoin client
# Copyright (C) 2014 Thomas Voegtlin
#
2016-02-23 11:36:42 +01:00
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
2014-06-10 14:32:17 +02:00
#
2016-02-23 11:36:42 +01:00
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
2014-06-10 14:32:17 +02:00
#
2016-02-23 11:36:42 +01:00
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
2014-05-05 17:31:39 +02:00
import hashlib
import sys
import time
2019-10-23 17:09:41 +02:00
from typing import Optional , List
2019-08-27 18:03:01 +02:00
import asyncio
import urllib . parse
2014-05-05 17:31:39 +02:00
2018-12-13 23:11:59 +01:00
import certifi
2018-11-05 19:31:17 +01:00
import aiohttp
2017-01-22 21:25:24 +03:00
2014-05-05 17:31:39 +02:00
try :
2017-02-25 13:36:24 +01:00
from . import paymentrequest_pb2 as pb2
2015-01-27 10:01:40 +01:00
except ImportError :
2018-07-11 17:38:47 +02:00
sys . exit ( " Error: could not find paymentrequest_pb2.py. Create it with ' protoc --proto_path=electrum/ --python_out=electrum/ electrum/paymentrequest.proto ' " )
2014-05-05 17:31:39 +02:00
2018-07-11 17:38:47 +02:00
from . import bitcoin , ecc , util , transaction , x509 , rsakey
2019-04-26 18:52:26 +02:00
from . util import bh2u , bfh , export_meta , import_meta , make_aiohttp_session
2019-01-28 11:14:30 +01:00
from . util import PR_UNPAID , PR_EXPIRED , PR_PAID , PR_UNKNOWN , PR_INFLIGHT
2018-10-25 22:20:33 +02:00
from . crypto import sha256
2019-10-23 17:09:41 +02:00
from . bitcoin import address_to_script
from . transaction import PartialTxOutput
2018-11-05 19:31:17 +01:00
from . network import Network
2019-04-26 18:52:26 +02:00
from . logging import get_logger , Logger
_logger = get_logger ( __name__ )
2018-11-05 19:31:17 +01:00
2014-05-07 15:26:38 +02:00
2014-05-05 17:31:39 +02:00
REQUEST_HEADERS = { ' Accept ' : ' application/bitcoin-paymentrequest ' , ' User-Agent ' : ' Electrum ' }
ACK_HEADERS = { ' Content-Type ' : ' application/bitcoin-payment ' , ' Accept ' : ' application/bitcoin-paymentack ' , ' User-Agent ' : ' Electrum ' }
2018-12-13 23:11:59 +01:00
ca_path = certifi . where ( )
2017-02-23 08:55:47 +01:00
ca_list = None
ca_keyID = None
def load_ca_list ( ) :
global ca_list , ca_keyID
if ca_list is None :
ca_list , ca_keyID = x509 . load_certificates ( ca_path )
2014-05-05 17:31:39 +02:00
2014-06-10 14:32:17 +02:00
2014-06-07 19:53:54 +02:00
2018-11-05 19:31:17 +01:00
async def get_payment_request ( url : str ) - > ' PaymentRequest ' :
2017-09-04 14:43:31 +02:00
u = urllib . parse . urlparse ( url )
2017-01-16 10:18:00 +01:00
error = None
2018-11-05 19:31:17 +01:00
if u . scheme in ( ' http ' , ' https ' ) :
resp_content = None
2016-12-21 17:19:27 +01:00
try :
2018-11-05 19:31:17 +01:00
proxy = Network . get_instance ( ) . proxy
async with make_aiohttp_session ( proxy , headers = REQUEST_HEADERS ) as session :
async with session . get ( url ) as response :
resp_content = await response . read ( )
response . raise_for_status ( )
# Guard against `bitcoin:`-URIs with invalid payment request URLs
if " Content-Type " not in response . headers \
or response . headers [ " Content-Type " ] != " application/bitcoin-paymentrequest " :
data = None
error = " payment URL not pointing to a payment request handling server "
else :
data = resp_content
data_len = len ( data ) if data is not None else None
2019-04-26 18:52:26 +02:00
_logger . info ( f ' fetched payment request { url } { data_len } ' )
2019-08-27 18:03:01 +02:00
except ( aiohttp . ClientError , asyncio . TimeoutError ) as e :
2019-06-05 19:40:33 +02:00
error = f " Error while contacting payment URL: { url } . \n error type: { type ( e ) } "
if isinstance ( e , aiohttp . ClientResponseError ) :
error + = f " \n Got HTTP status code { e . status } . "
if resp_content :
try :
error_text_received = resp_content . decode ( " utf8 " )
except UnicodeDecodeError :
error_text_received = " (failed to decode error) "
else :
error_text_received = error_text_received [ : 400 ]
error_oneline = ' -- ' . join ( error . split ( ' \n ' ) )
_logger . info ( f " { error_oneline } -- [DO NOT TRUST THIS MESSAGE] "
f " { repr ( e ) } text: { error_text_received } " )
2016-12-21 17:19:27 +01:00
data = None
2015-04-13 17:54:28 +02:00
elif u . scheme == ' file ' :
2016-12-21 17:19:27 +01:00
try :
2018-03-23 21:47:51 +01:00
with open ( u . path , ' r ' , encoding = ' utf-8 ' ) as f :
2016-12-21 17:19:27 +01:00
data = f . read ( )
except IOError :
data = None
error = " payment URL not pointing to a valid file "
2015-04-13 17:54:28 +02:00
else :
2018-08-15 13:43:19 +02:00
data = None
2018-11-05 19:31:17 +01:00
error = f " Unknown scheme for payment request. URL: { url } "
2019-06-05 19:05:58 +02:00
pr = PaymentRequest ( data , error = error )
2014-11-11 11:08:25 +01:00
return pr
2014-06-07 19:53:54 +02:00
2014-11-11 11:08:25 +01:00
class PaymentRequest :
2014-06-07 19:53:54 +02:00
2019-06-05 19:05:58 +02:00
def __init__ ( self , data , * , error = None ) :
2014-11-11 11:08:25 +01:00
self . raw = data
2019-06-05 19:05:58 +02:00
self . error = error # FIXME overloaded and also used when 'verify' succeeds
2014-11-11 11:08:25 +01:00
self . parse ( data )
2015-04-22 13:59:38 +02:00
self . requestor = None # known after verify
2014-11-11 11:08:25 +01:00
self . tx = None
2014-06-07 19:53:54 +02:00
2014-11-11 11:08:25 +01:00
def __str__ ( self ) :
2018-07-02 02:36:19 +03:00
return str ( self . raw )
2014-06-07 19:53:54 +02:00
def parse ( self , r ) :
2019-10-23 17:09:41 +02:00
self . outputs = [ ] # type: List[PartialTxOutput]
2016-12-21 17:19:27 +01:00
if self . error :
return
2018-10-25 22:20:33 +02:00
self . id = bh2u ( sha256 ( r ) [ 0 : 16 ] )
2014-06-06 16:16:14 +02:00
try :
2015-04-05 18:57:00 +02:00
self . data = pb2 . PaymentRequest ( )
2014-06-06 16:16:14 +02:00
self . data . ParseFromString ( r )
2014-05-07 18:20:17 +02:00
except :
self . error = " cannot parse payment request "
return
2015-04-10 16:32:16 +02:00
self . details = pb2 . PaymentDetails ( )
self . details . ParseFromString ( self . data . serialized_payment_details )
for o in self . details . outputs :
2019-10-23 17:09:41 +02:00
addr = transaction . get_address_from_output_script ( o . script )
if not addr :
2018-11-14 16:04:43 +01:00
# TODO maybe rm restriction but then get_requestor and get_id need changes
self . error = " only addresses are allowed as outputs "
return
2019-10-23 17:09:41 +02:00
self . outputs . append ( PartialTxOutput . from_address_and_value ( addr , o . amount ) )
2015-04-10 16:32:16 +02:00
self . memo = self . details . memo
self . payment_url = self . details . payment_url
2014-05-05 17:31:39 +02:00
2015-07-07 08:59:03 +02:00
def verify ( self , contacts ) :
2016-12-21 17:19:27 +01:00
if self . error :
return False
2015-06-03 09:34:35 +02:00
if not self . raw :
self . error = " Empty request "
2016-12-21 17:19:27 +01:00
return False
2015-07-07 08:59:03 +02:00
pr = pb2 . PaymentRequest ( )
2016-10-14 06:38:49 +02:00
try :
pr . ParseFromString ( self . raw )
except :
self . error = " Error: Cannot parse payment request "
2016-12-21 17:19:27 +01:00
return False
2015-07-07 08:59:03 +02:00
if not pr . signature :
2018-04-15 20:45:30 +03:00
# the address will be displayed as requestor
2016-02-12 12:01:13 +01:00
self . requestor = None
return True
2015-07-07 08:59:03 +02:00
if pr . pki_type in [ " x509+sha256 " , " x509+sha1 " ] :
return self . verify_x509 ( pr )
elif pr . pki_type in [ " dnssec+btc " , " dnssec+ecdsa " ] :
return self . verify_dnssec ( pr , contacts )
else :
self . error = " ERROR: Unsupported PKI Type for Message Signature "
return False
def verify_x509 ( self , paymntreq ) :
2017-02-23 08:55:47 +01:00
load_ca_list ( )
2015-07-07 08:59:03 +02:00
if not ca_list :
self . error = " Trusted certificate authorities list not found "
return False
2015-04-05 18:57:00 +02:00
cert = pb2 . X509Certificates ( )
2014-05-05 17:31:39 +02:00
cert . ParseFromString ( paymntreq . pki_data )
2015-08-05 20:49:45 +02:00
# verify the chain of certificates
try :
x , ca = verify_cert_chain ( cert . certificate )
except BaseException as e :
2019-04-26 18:52:26 +02:00
_logger . exception ( ' ' )
2015-08-05 20:49:45 +02:00
self . error = str ( e )
2014-05-05 17:31:39 +02:00
return False
2015-08-05 20:49:45 +02:00
# get requestor name
self . requestor = x . get_common_name ( )
if self . requestor . startswith ( ' *. ' ) :
self . requestor = self . requestor [ 2 : ]
2015-04-10 16:32:16 +02:00
# verify the BIP70 signature
2015-08-04 18:16:06 +02:00
pubkey0 = rsakey . RSAKey ( x . modulus , x . exponent )
2014-06-10 14:32:17 +02:00
sig = paymntreq . signature
2017-02-18 20:22:46 +01:00
paymntreq . signature = b ' '
2014-05-05 17:31:39 +02:00
s = paymntreq . SerializeToString ( )
2014-06-10 14:32:17 +02:00
sigBytes = bytearray ( sig )
msgBytes = bytearray ( s )
2014-05-05 17:31:39 +02:00
if paymntreq . pki_type == " x509+sha256 " :
2014-06-10 14:32:17 +02:00
hashBytes = bytearray ( hashlib . sha256 ( msgBytes ) . digest ( ) )
2015-02-08 20:04:42 +01:00
verify = pubkey0 . verify ( sigBytes , x509 . PREFIX_RSA_SHA256 + hashBytes )
2014-05-05 17:31:39 +02:00
elif paymntreq . pki_type == " x509+sha1 " :
2014-06-10 14:32:17 +02:00
verify = pubkey0 . hashAndVerify ( sigBytes , msgBytes )
2018-11-14 16:04:43 +01:00
else :
self . error = f " ERROR: unknown pki_type { paymntreq . pki_type } in Payment Request "
return False
2014-06-10 14:32:17 +02:00
if not verify :
2014-05-07 18:10:14 +02:00
self . error = " ERROR: Invalid Signature for Payment Request Data "
2014-05-05 17:31:39 +02:00
return False
### SIG Verified
2015-04-13 17:54:28 +02:00
self . error = ' Signed by Trusted CA: ' + ca . get_common_name ( )
2014-05-07 18:10:14 +02:00
return True
2014-05-05 17:31:39 +02:00
2015-07-07 08:59:03 +02:00
def verify_dnssec ( self , pr , contacts ) :
sig = pr . signature
alias = pr . pki_data
info = contacts . resolve ( alias )
if info . get ( ' validated ' ) is not True :
self . error = " Alias verification failed (DNSSEC) "
return False
if pr . pki_type == " dnssec+btc " :
self . requestor = alias
address = info . get ( ' address ' )
2018-05-24 18:57:13 +02:00
pr . signature = b ' '
2015-07-07 08:59:03 +02:00
message = pr . SerializeToString ( )
2018-05-24 18:57:13 +02:00
if ecc . verify_message_with_address ( address , sig , message ) :
2015-07-07 08:59:03 +02:00
self . error = ' Verified with DNSSEC '
return True
else :
self . error = " verify failed "
return False
else :
self . error = " unknown algo "
return False
2019-06-05 19:05:58 +02:00
def has_expired ( self ) - > Optional [ bool ] :
if not hasattr ( self , ' details ' ) :
return None
2014-06-07 19:53:54 +02:00
return self . details . expires and self . details . expires < int ( time . time ( ) )
2014-05-05 17:31:39 +02:00
2019-01-30 11:10:11 +01:00
def get_time ( self ) :
return self . details . time
2014-06-13 16:53:43 +02:00
def get_expiration_date ( self ) :
return self . details . expires
2014-06-06 16:16:14 +02:00
def get_amount ( self ) :
kivy: fix paying onchain invoices
when pasting a new invoice and paying it
Traceback (most recent call last):
File "/home/user/wspace/electrum/electrum/gui/kivy/uix/screens.py", line 358, in _do_send_onchain
tx = self.app.wallet.make_unsigned_transaction(coins, outputs, None)
File "/home/user/wspace/electrum/electrum/wallet.py", line 849, in make_unsigned_transaction
if o.type == TYPE_ADDRESS:
AttributeError: 'tuple' object has no attribute 'type'
when loading back a saved invoice
Traceback (most recent call last):
File "/home/user/wspace/electrum/electrum/gui/kivy/uix/screens.py", line 358, in _do_send_onchain
tx = self.app.wallet.make_unsigned_transaction(coins, outputs, None)
File "/home/user/wspace/electrum/electrum/wallet.py", line 849, in make_unsigned_transaction
if o.type == TYPE_ADDRESS:
AttributeError: 'list' object has no attribute 'type'
2019-09-13 15:00:34 +02:00
return sum ( map ( lambda x : x . value , self . outputs ) )
2014-06-06 16:16:14 +02:00
2016-02-09 12:48:25 +01:00
def get_address ( self ) :
o = self . outputs [ 0 ]
2019-10-23 17:09:41 +02:00
addr = o . address
assert addr
return addr
2016-02-09 12:48:25 +01:00
2015-04-22 13:59:38 +02:00
def get_requestor ( self ) :
2016-02-09 12:48:25 +01:00
return self . requestor if self . requestor else self . get_address ( )
2014-11-11 11:08:25 +01:00
def get_verify_status ( self ) :
2016-02-13 11:00:00 +01:00
return self . error if self . requestor else " No Signature "
2014-06-06 16:16:14 +02:00
2014-06-12 10:20:06 +02:00
def get_memo ( self ) :
return self . memo
2014-06-06 16:16:14 +02:00
def get_id ( self ) :
2016-02-09 12:48:25 +01:00
return self . id if self . requestor else self . get_address ( )
2014-05-05 17:31:39 +02:00
2014-06-07 19:53:54 +02:00
def get_outputs ( self ) :
2014-06-12 11:27:18 +02:00
return self . outputs [ : ]
2014-06-07 19:53:54 +02:00
2018-11-05 19:31:17 +01:00
async def send_payment_and_receive_paymentack ( self , raw_tx , refund_addr ) :
2014-06-07 19:53:54 +02:00
pay_det = self . details
if not self . details . payment_url :
2014-05-10 20:35:00 +02:00
return False , " no url "
2015-06-23 16:58:21 +02:00
paymnt = pb2 . Payment ( )
2014-05-07 18:59:51 +02:00
paymnt . merchant_data = pay_det . merchant_data
2017-08-13 12:00:33 +02:00
paymnt . transactions . append ( bfh ( raw_tx ) )
2014-05-07 18:59:51 +02:00
ref_out = paymnt . refund_to . add ( )
2019-10-23 17:09:41 +02:00
ref_out . script = util . bfh ( address_to_script ( refund_addr ) )
2014-05-07 18:59:51 +02:00
paymnt . memo = " Paid using Electrum "
pm = paymnt . SerializeToString ( )
2017-09-04 14:43:31 +02:00
payurl = urllib . parse . urlparse ( pay_det . payment_url )
2018-11-05 19:31:17 +01:00
resp_content = None
2014-05-07 18:59:51 +02:00
try :
2018-11-05 19:31:17 +01:00
proxy = Network . get_instance ( ) . proxy
async with make_aiohttp_session ( proxy , headers = ACK_HEADERS ) as session :
async with session . post ( payurl . geturl ( ) , data = pm ) as response :
resp_content = await response . read ( )
response . raise_for_status ( )
try :
paymntack = pb2 . PaymentACK ( )
paymntack . ParseFromString ( resp_content )
except Exception :
return False , " PaymentACK could not be processed. Payment was sent; please manually verify that payment was received. "
print ( f " PaymentACK message received: { paymntack . memo } " )
return True , paymntack . memo
except aiohttp . ClientError as e :
2019-06-05 19:40:33 +02:00
error = f " Payment Message/PaymentACK Failed: \n error type: { type ( e ) } "
if isinstance ( e , aiohttp . ClientResponseError ) :
error + = f " \n Got HTTP status code { e . status } . "
if resp_content :
try :
error_text_received = resp_content . decode ( " utf8 " )
except UnicodeDecodeError :
error_text_received = " (failed to decode error) "
else :
error_text_received = error_text_received [ : 400 ]
error_oneline = ' -- ' . join ( error . split ( ' \n ' ) )
_logger . info ( f " { error_oneline } -- [DO NOT TRUST THIS MESSAGE] "
f " { repr ( e ) } text: { error_text_received } " )
2018-11-05 19:31:17 +01:00
return False , error
2014-05-05 17:31:39 +02:00
2015-07-11 20:26:30 +02:00
def make_unsigned_request ( req ) :
2017-01-22 21:25:24 +03:00
from . transaction import Transaction
2015-07-11 20:26:30 +02:00
addr = req [ ' address ' ]
2015-07-21 16:30:25 +02:00
time = req . get ( ' time ' , 0 )
2015-07-22 15:33:50 +02:00
exp = req . get ( ' exp ' , 0 )
2015-07-22 15:28:43 +02:00
if time and type ( time ) != int :
2015-07-22 15:24:15 +02:00
time = 0
2015-07-22 15:28:43 +02:00
if exp and type ( exp ) != int :
2015-07-22 15:24:15 +02:00
exp = 0
2015-07-11 20:26:30 +02:00
amount = req [ ' amount ' ]
2015-07-13 21:37:41 +02:00
if amount is None :
amount = 0
2015-07-11 20:26:30 +02:00
memo = req [ ' memo ' ]
2019-10-23 17:09:41 +02:00
script = bfh ( address_to_script ( addr ) )
2015-07-11 20:26:30 +02:00
outputs = [ ( script , amount ) ]
2015-04-05 18:57:00 +02:00
pd = pb2 . PaymentDetails ( )
2015-04-21 08:45:51 +02:00
for script , amount in outputs :
pd . outputs . add ( amount = amount , script = script )
pd . time = time
2015-07-22 15:33:50 +02:00
pd . expires = time + exp if exp else 0
2015-04-05 18:57:00 +02:00
pd . memo = memo
pr = pb2 . PaymentRequest ( )
pr . serialized_payment_details = pd . SerializeToString ( )
2017-10-04 02:12:52 +02:00
pr . signature = util . to_bytes ( ' ' )
2015-07-11 20:26:30 +02:00
return pr
2015-07-07 08:59:03 +02:00
2015-07-11 20:26:30 +02:00
def sign_request_with_alias ( pr , alias , alias_privkey ) :
pr . pki_type = ' dnssec+btc '
pr . pki_data = str ( alias )
message = pr . SerializeToString ( )
2018-05-24 18:57:13 +02:00
ec_key = ecc . ECPrivkey ( alias_privkey )
2018-11-18 22:07:27 +01:00
compressed = bitcoin . is_compressed_privkey ( alias_privkey )
2018-05-24 18:57:13 +02:00
pr . signature = ec_key . sign_message ( message , compressed )
2015-07-09 14:15:30 +02:00
2015-07-11 20:26:30 +02:00
2015-08-05 20:49:45 +02:00
def verify_cert_chain ( chain ) :
""" Verify a chain of certificates. The last certificate is the CA """
2017-02-23 08:55:47 +01:00
load_ca_list ( )
2015-08-05 20:49:45 +02:00
# parse the chain
cert_num = len ( chain )
x509_chain = [ ]
for i in range ( cert_num ) :
2015-08-07 11:39:30 +02:00
x = x509 . X509 ( bytearray ( chain [ i ] ) )
2015-08-05 20:49:45 +02:00
x509_chain . append ( x )
if i == 0 :
x . check_date ( )
else :
if not x . check_ca ( ) :
2018-04-07 17:10:30 +02:00
raise Exception ( " ERROR: Supplied CA Certificate Error " )
2015-08-05 20:49:45 +02:00
if not cert_num > 1 :
2018-04-07 17:10:30 +02:00
raise Exception ( " ERROR: CA Certificate Chain Not Provided by Payment Processor " )
2015-08-05 20:49:45 +02:00
# if the root CA is not supplied, add it to the chain
ca = x509_chain [ cert_num - 1 ]
if ca . getFingerprint ( ) not in ca_list :
keyID = ca . get_issuer_keyID ( )
f = ca_keyID . get ( keyID )
if f :
root = ca_list [ f ]
x509_chain . append ( root )
else :
2018-04-07 17:10:30 +02:00
raise Exception ( " Supplied CA Not Found in Trusted CA Store. " )
2015-08-05 20:49:45 +02:00
# verify the chain of signatures
cert_num = len ( x509_chain )
for i in range ( 1 , cert_num ) :
x = x509_chain [ i ]
prev_x = x509_chain [ i - 1 ]
algo , sig , data = prev_x . get_signature ( )
sig = bytearray ( sig )
pubkey = rsakey . RSAKey ( x . modulus , x . exponent )
if algo == x509 . ALGO_RSA_SHA1 :
verify = pubkey . hashAndVerify ( sig , data )
elif algo == x509 . ALGO_RSA_SHA256 :
hashBytes = bytearray ( hashlib . sha256 ( data ) . digest ( ) )
verify = pubkey . verify ( sig , x509 . PREFIX_RSA_SHA256 + hashBytes )
elif algo == x509 . ALGO_RSA_SHA384 :
hashBytes = bytearray ( hashlib . sha384 ( data ) . digest ( ) )
verify = pubkey . verify ( sig , x509 . PREFIX_RSA_SHA384 + hashBytes )
elif algo == x509 . ALGO_RSA_SHA512 :
hashBytes = bytearray ( hashlib . sha512 ( data ) . digest ( ) )
verify = pubkey . verify ( sig , x509 . PREFIX_RSA_SHA512 + hashBytes )
else :
2018-09-17 14:44:01 +02:00
raise Exception ( " Algorithm not supported: {} " . format ( algo ) )
2015-08-05 20:49:45 +02:00
if not verify :
2018-04-07 17:10:30 +02:00
raise Exception ( " Certificate not Signed by Provided CA Certificate Chain " )
2015-08-05 20:49:45 +02:00
return x509_chain [ 0 ] , ca
def check_ssl_config ( config ) :
2017-01-22 21:25:24 +03:00
from . import pem
2019-09-05 11:36:50 +02:00
key_path = config . get ( ' ssl_keyfile ' )
cert_path = config . get ( ' ssl_certfile ' )
2018-03-23 21:47:51 +01:00
with open ( key_path , ' r ' , encoding = ' utf-8 ' ) as f :
2015-08-05 20:49:45 +02:00
params = pem . parse_private_key ( f . read ( ) )
2018-03-23 21:47:51 +01:00
with open ( cert_path , ' r ' , encoding = ' utf-8 ' ) as f :
2015-08-05 20:49:45 +02:00
s = f . read ( )
2015-08-05 20:59:51 +02:00
bList = pem . dePemList ( s , " CERTIFICATE " )
2015-08-05 20:49:45 +02:00
# verify chain
x , ca = verify_cert_chain ( bList )
2015-08-05 20:59:51 +02:00
# verify that privkey and pubkey match
privkey = rsakey . RSAKey ( * params )
pubkey = rsakey . RSAKey ( x . modulus , x . exponent )
assert x . modulus == params [ 0 ]
assert x . exponent == params [ 1 ]
# return requestor
requestor = x . get_common_name ( )
if requestor . startswith ( ' *. ' ) :
requestor = requestor [ 2 : ]
return requestor
2015-08-05 20:49:45 +02:00
2015-07-11 21:09:56 +02:00
def sign_request_with_x509 ( pr , key_path , cert_path ) :
2017-01-22 21:25:24 +03:00
from . import pem
2018-03-23 21:47:51 +01:00
with open ( key_path , ' r ' , encoding = ' utf-8 ' ) as f :
2015-08-04 18:16:06 +02:00
params = pem . parse_private_key ( f . read ( ) )
privkey = rsakey . RSAKey ( * params )
2018-03-23 21:47:51 +01:00
with open ( cert_path , ' r ' , encoding = ' utf-8 ' ) as f :
2015-08-04 18:16:06 +02:00
s = f . read ( )
bList = pem . dePemList ( s , " CERTIFICATE " )
2015-07-11 20:26:30 +02:00
certificates = pb2 . X509Certificates ( )
2017-11-13 11:10:51 +01:00
certificates . certificate . extend ( map ( bytes , bList ) )
2015-07-11 20:26:30 +02:00
pr . pki_type = ' x509+sha256 '
pr . pki_data = certificates . SerializeToString ( )
msgBytes = bytearray ( pr . SerializeToString ( ) )
hashBytes = bytearray ( hashlib . sha256 ( msgBytes ) . digest ( ) )
2015-08-04 18:16:06 +02:00
sig = privkey . sign ( x509 . PREFIX_RSA_SHA256 + hashBytes )
2015-07-11 20:26:30 +02:00
pr . signature = bytes ( sig )
def serialize_request ( req ) :
pr = make_unsigned_request ( req )
2015-07-21 11:40:55 +02:00
signature = req . get ( ' sig ' )
2015-07-21 12:26:37 +02:00
requestor = req . get ( ' name ' )
2015-07-21 11:40:55 +02:00
if requestor and signature :
2017-01-30 12:36:56 +03:00
pr . signature = bfh ( signature )
2015-07-11 20:26:30 +02:00
pr . pki_type = ' dnssec+btc '
pr . pki_data = str ( requestor )
return pr
2015-04-05 18:57:00 +02:00
2015-07-22 09:06:03 +02:00
def make_request ( config , req ) :
2015-07-11 20:26:30 +02:00
pr = make_unsigned_request ( req )
2019-09-05 10:57:50 +02:00
key_path = config . get ( ' ssl_keyfile ' )
cert_path = config . get ( ' ssl_certfile ' )
2015-07-11 20:26:30 +02:00
if key_path and cert_path :
sign_request_with_x509 ( pr , key_path , cert_path )
2015-07-22 09:06:03 +02:00
return pr