electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

pem.py (6661B)


      1 #!/usr/bin/env python
      2 #
      3 # Electrum - lightweight Bitcoin client
      4 # Copyright (C) 2015 Thomas Voegtlin
      5 #
      6 # Permission is hereby granted, free of charge, to any person
      7 # obtaining a copy of this software and associated documentation files
      8 # (the "Software"), to deal in the Software without restriction,
      9 # including without limitation the rights to use, copy, modify, merge,
     10 # publish, distribute, sublicense, and/or sell copies of the Software,
     11 # and to permit persons to whom the Software is furnished to do so,
     12 # subject to the following conditions:
     13 #
     14 # The above copyright notice and this permission notice shall be
     15 # included in all copies or substantial portions of the Software.
     16 #
     17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     18 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     19 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     20 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
     21 # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
     22 # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     23 # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     24 # SOFTWARE.
     25 
     26 
     27 # This module uses code from TLSLlite
     28 # TLSLite Author: Trevor Perrin)
     29 
     30 
     31 import binascii
     32 
     33 from .x509 import ASN1_Node, bytestr_to_int, decode_OID
     34 
     35 
     36 def a2b_base64(s):
     37     try:
     38         b = bytearray(binascii.a2b_base64(s))
     39     except Exception as e:
     40         raise SyntaxError("base64 error: %s" % e)
     41     return b
     42 
     43 def b2a_base64(b):
     44     return binascii.b2a_base64(b)
     45 
     46 
     47 def dePem(s, name):
     48     """Decode a PEM string into a bytearray of its payload.
     49     
     50     The input must contain an appropriate PEM prefix and postfix
     51     based on the input name string, e.g. for name="CERTIFICATE":
     52 
     53     -----BEGIN CERTIFICATE-----
     54     MIIBXDCCAUSgAwIBAgIBADANBgkqhkiG9w0BAQUFADAPMQ0wCwYDVQQDEwRUQUNL
     55     ...
     56     KoZIhvcNAQEFBQADAwA5kw==
     57     -----END CERTIFICATE-----    
     58 
     59     The first such PEM block in the input will be found, and its
     60     payload will be base64 decoded and returned.
     61     """
     62     prefix  = "-----BEGIN %s-----" % name
     63     postfix = "-----END %s-----" % name    
     64     start = s.find(prefix)
     65     if start == -1:
     66         raise SyntaxError("Missing PEM prefix")
     67     end = s.find(postfix, start+len(prefix))
     68     if end == -1:
     69         raise SyntaxError("Missing PEM postfix")
     70     s = s[start+len("-----BEGIN %s-----" % name) : end]
     71     retBytes = a2b_base64(s) # May raise SyntaxError
     72     return retBytes
     73 
     74 def dePemList(s, name):
     75     """Decode a sequence of PEM blocks into a list of bytearrays.
     76 
     77     The input must contain any number of PEM blocks, each with the appropriate
     78     PEM prefix and postfix based on the input name string, e.g. for
     79     name="TACK BREAK SIG".  Arbitrary text can appear between and before and
     80     after the PEM blocks.  For example:
     81 
     82     " Created by TACK.py 0.9.3 Created at 2012-02-01T00:30:10Z -----BEGIN TACK
     83     BREAK SIG-----
     84     ATKhrz5C6JHJW8BF5fLVrnQss6JnWVyEaC0p89LNhKPswvcC9/s6+vWLd9snYTUv
     85     YMEBdw69PUP8JB4AdqA3K6Ap0Fgd9SSTOECeAKOUAym8zcYaXUwpk0+WuPYa7Zmm
     86     SkbOlK4ywqt+amhWbg9txSGUwFO5tWUHT3QrnRlE/e3PeNFXLx5Bckg= -----END TACK
     87     BREAK SIG----- Created by TACK.py 0.9.3 Created at 2012-02-01T00:30:11Z
     88     -----BEGIN TACK BREAK SIG-----
     89     ATKhrz5C6JHJW8BF5fLVrnQss6JnWVyEaC0p89LNhKPswvcC9/s6+vWLd9snYTUv
     90     YMEBdw69PUP8JB4AdqA3K6BVCWfcjN36lx6JwxmZQncS6sww7DecFO/qjSePCxwM
     91     +kdDqX/9/183nmjx6bf0ewhPXkA0nVXsDYZaydN8rJU1GaMlnjcIYxY= -----END TACK
     92     BREAK SIG----- "
     93     
     94     All such PEM blocks will be found, decoded, and return in an ordered list
     95     of bytearrays, which may have zero elements if not PEM blocks are found.
     96      """
     97     bList = []
     98     prefix  = "-----BEGIN %s-----" % name
     99     postfix = "-----END %s-----" % name
    100     while 1:
    101         start = s.find(prefix)
    102         if start == -1:
    103             return bList
    104         end = s.find(postfix, start+len(prefix))
    105         if end == -1:
    106             raise SyntaxError("Missing PEM postfix")
    107         s2 = s[start+len(prefix) : end]
    108         retBytes = a2b_base64(s2) # May raise SyntaxError
    109         bList.append(retBytes)
    110         s = s[end+len(postfix) : ]
    111 
    112 def pem(b, name):
    113     """Encode a payload bytearray into a PEM string.
    114     
    115     The input will be base64 encoded, then wrapped in a PEM prefix/postfix
    116     based on the name string, e.g. for name="CERTIFICATE":
    117     
    118     -----BEGIN CERTIFICATE-----
    119     MIIBXDCCAUSgAwIBAgIBADANBgkqhkiG9w0BAQUFADAPMQ0wCwYDVQQDEwRUQUNL
    120     ...
    121     KoZIhvcNAQEFBQADAwA5kw==
    122     -----END CERTIFICATE-----    
    123     """
    124     s1 = b2a_base64(b)[:-1] # remove terminating \n
    125     s2 = b""
    126     while s1:
    127         s2 += s1[:64] + b"\n"
    128         s1 = s1[64:]
    129     s = ("-----BEGIN %s-----\n" % name).encode('ascii') + s2 + \
    130         ("-----END %s-----\n" % name).encode('ascii')
    131     return s
    132 
    133 def pemSniff(inStr, name):
    134     searchStr = "-----BEGIN %s-----" % name
    135     return searchStr in inStr
    136 
    137 
    138 def parse_private_key(s):
    139     """Parse a string containing a PEM-encoded <privateKey>."""
    140     if pemSniff(s, "PRIVATE KEY"):
    141         bytes = dePem(s, "PRIVATE KEY")
    142         return _parsePKCS8(bytes)
    143     elif pemSniff(s, "RSA PRIVATE KEY"):
    144         bytes = dePem(s, "RSA PRIVATE KEY")
    145         return _parseSSLeay(bytes)
    146     else:
    147         raise SyntaxError("Not a PEM private key file")
    148 
    149 
    150 def _parsePKCS8(_bytes):
    151     s = ASN1_Node(_bytes)
    152     root = s.root()
    153     version_node = s.first_child(root)
    154     version = bytestr_to_int(s.get_value_of_type(version_node, 'INTEGER'))
    155     if version != 0:
    156         raise SyntaxError("Unrecognized PKCS8 version")
    157     rsaOID_node = s.next_node(version_node)
    158     ii = s.first_child(rsaOID_node)
    159     rsaOID = decode_OID(s.get_value_of_type(ii, 'OBJECT IDENTIFIER'))
    160     if rsaOID != '1.2.840.113549.1.1.1':
    161         raise SyntaxError("Unrecognized AlgorithmIdentifier")
    162     privkey_node = s.next_node(rsaOID_node)
    163     value = s.get_value_of_type(privkey_node, 'OCTET STRING')
    164     return _parseASN1PrivateKey(value)
    165 
    166 
    167 def _parseSSLeay(bytes):
    168     return _parseASN1PrivateKey(ASN1_Node(bytes))
    169 
    170 
    171 def bytesToNumber(s):
    172     return int(binascii.hexlify(s), 16)
    173 
    174 
    175 def _parseASN1PrivateKey(s):
    176     s = ASN1_Node(s)
    177     root = s.root()
    178     version_node = s.first_child(root)
    179     version = bytestr_to_int(s.get_value_of_type(version_node, 'INTEGER'))
    180     if version != 0:
    181         raise SyntaxError("Unrecognized RSAPrivateKey version")
    182     n = s.next_node(version_node)
    183     e = s.next_node(n)
    184     d = s.next_node(e)
    185     p = s.next_node(d)
    186     q = s.next_node(p)
    187     dP = s.next_node(q)
    188     dQ = s.next_node(dP)
    189     qInv = s.next_node(dQ)
    190     return list(map(lambda x: bytesToNumber(s.get_value_of_type(x, 'INTEGER')), [n, e, d, p, q, dP, dQ, qInv]))
    191