-
+ 33671E1F41334901B94EC0FC991EFA42F52C1619B332ABBC0CED336E9AD15BA270E116BDDE43185A80394D31BBBF498549A88F6AF2E064DE367A8D90D0E3ED07makepgp.py(0 . 0)(1 . 148)
5 import struct
6 import time
7 import sys
8 import base64
9 import math
10
11 openpgp_publickey_format = ">BIB"
12 mpi_format = ">H"
13 packet_length_format = ">I"
14 crc_format = ">I"
15
16 # determine the index of the highest bit set to 1
17 def count_bits(B):
18 R = 0
19 i = 0
20 while B > 0:
21 i += 1
22 if B & 0x1:
23 R = i
24 B >>= 1
25 return R
26
27 # convert a number to an array of bytes
28 # the bytes in the array are stored in big-endian order.
29 # the most significant byte is stored as the first item
30 # in the array
31 def number_to_bytes(B):
32 R = []
33 bits = 0
34 while B > 0xff:
35 bits += 8
36 R.append(B & 0xff)
37 B >>= 8
38 R.append(B)
39 bits += count_bits(B)
40 return bits, ''.join(map(chr, reversed(R)))
41
42 # An MPI is a byte array that starts with a two byte
43 # length header. The length is given in bits
44 def number_to_mpi(B):
45 C, A = number_to_bytes(B)
46 return struct.pack(mpi_format, C) + A
47
48 # A PGP public key header consists of a byte "4",
49 # an integer (4 byte) timestamp and a byte "1" (RSA)
50 def public_key_header(T):
51 return struct.pack(openpgp_publickey_format, 4, T, 1)
52
53 # A public key packet is the public key header
54 # plus 2 MPI numbers, the RSA modulus N and
55 # the RSA exponent e.
56 def public_key_packet(t, n, e):
57 return ''.join((public_key_header(t), number_to_mpi(n), number_to_mpi(e),))
58
59 # A comment or userid packet is a string encoded as utf-8
60 def userid_packet(s):
61 return s.encode('utf8')
62
63 # The PGP format is a stream of "packets".
64 # Each packet has a header. This header consists of a tag
65 # and a length field. The tag has flags to determine if it is a
66 # "new" or "old" packet. The only supported encoding is "new".
67 def encode_packet(packet_bytes, tag = 6):
68 # 0x80, 8th bit always set, 7th bit set --> new packet
69 h = 0x80 | 0x40
70 # 0-5 bits -> the tag
71 h |= tag
72 header = chr(h)
73
74 # dude, this is totally how you may save 2 or 3 bytes with minimal complexity
75 l = len(packet_bytes)
76 if l < 192:
77 header += chr(l)
78 elif l < 8384:
79 l -= 192
80 o1 = l >> 0xff
81 o2 = l & 0xff
82 header += chr(o1 + 192) + chr(o2)
83 else:
84 header += chr(0xff) + struct.pack(packet_length_format, l)
85
86 return header + packet_bytes
87
88 # When you encode binary data as an ascii text with base64
89 # this data becomes fragile. So a CRC code is needed to
90 # fix this.
91 def crc24(s):
92 R = 0xB704CE
93 for char in s:
94 B = ord(char)
95 R ^= B << 16
96 for i in range(8):
97 R <<= 1;
98 if R & 0x1000000:
99 R ^= 0x1864CFB
100 return R & 0xFFFFFF
101
102 # Create a public key for consumption by Phuctor.
103 # The public key needs to contain 2 packets
104 # one for the key data (n, e)
105 # one for the comment
106 # It must be in the armor / ascii format.
107 def enarmored_public_key(n, e, comment, t):
108 R = []
109 # the header
110 R.append("-----BEGIN PGP PUBLIC KEY BLOCK-----")
111 R.append("")
112
113 # the packets in bytes
114 A = encode_packet(public_key_packet(t, n, e), 6)
115 A += encode_packet(userid_packet(comment), 13)
116
117 # the packets in base64 encoding with line length max 76
118 s=base64.b64encode(A)
119 i = 0
120 while i < len(s):
121 R.append(s[i:i+76])
122 i += 76
123
124 # the CRC
125 R.append("="+base64.b64encode(struct.pack(crc_format, crc24(A))[1:]))
126
127 # the footer
128 R.append("")
129 R.append("-----END PGP PUBLIC KEY BLOCK-----")
130
131 return '\n'.join(R)
132
133 # read a file with comma separated lines
134 # each line is in the TMSR format: e,n,comment
135 if __name__ == "__main__":
136 ser = 1
137 for x in sys.stdin:
138 x = x.strip()
139
140 # ignore empty lines
141 if len(x) == 0 or x.startswith('#'):
142 continue
143
144 # the comment may contain comma's so split on the first 2
145 e,n,comment = x.split(',', 2)
146
147 t0 = int(time.time())
148 with open("{0}.txt".format(ser), "wb") as stream:
149 stream.write(enarmored_public_key(int(n), int(e), comment, t0))
150
151 ser += 1
152