tpem.py - electrum - Electrum Bitcoin wallet
HTML git clone https://git.parazyd.org/electrum
DIR Log
DIR Files
DIR Refs
DIR Submodules
---
tpem.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