This file is indexed.

/usr/bin/openpgpkey is in hash-slinger 2.7-1.

This file is owned by root:root, with mode 0o755.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
#!/usr/bin/python
#
# openpgpkey: create OPENPGPKEY DNS record from a key in your keychain.
#
# Copyright 2012 - 2015 Paul Wouters <paul@cypherpunks.ca>
# Copyright 2015 Dirk Stoecker <github@dstoecker.de>
# Copyright 2015 Jan Vcelak <jv@fcelda.cz>
# Copyright 2015 Carsten Strotmann <cs@sys4.de>
# Copyright 2015 Ondrej Sury <ondrej@sury.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

VERSION="2.7"
OPENPGPKEY=61

import sys
import os
import gnupg
import unbound
import hashlib
import tempfile
import shutil

global ctx

def asctohex(s):
    empty = '' # I use this construct because I find ''.join() too dense
    return empty.join(['%02x' % ord(c) for c in s]) # the %02 pads when needed

def createOPENPGPKEY(email, gpgdisk, keyid, output, debug):
	ekey64 = "".join(gpgdisk.export_keys(keyid,minimal=True, secret=False, armor=True).split("\n")[2:-3])
	user,domain = email.lower().split("@")
	euser = sha256trunc(user)

	if output == "rfc" or output == "both":
		print "; keyid: %s" % keyid
		print "%s._openpgpkey.%s. IN OPENPGPKEY %s" % (euser, domain, ekey64)
	if output ==  "generic" or output == "both":
		if debug:
			print "Length for generic record is %s" % len(ekey)
		print "; keyid: %s" % keyid
		print "%s._openpgpkey.%s. IN TYPE61 \# %s %s" % (euser, domain, len(ekey), asctohex(ekey))

def sha256trunc(data):
	"""Compute SHA2-256 hash truncated to 28 octets."""
	return hashlib.sha256(data).hexdigest()[:56]

def getOPENPGPKEY(email,insecure_ok):
	"""This function queries for an OPENPGPKEY DNS Resource Record and compares it with the local gnupg keyring"""

	global ctx

	try:
		username, domainname = email.lower().split("@")
	except:
		sys.exit("Invalid email syntax")

	keyname = "%s._openpgpkey.%s"%(sha256trunc(username), domainname)

	status, result = ctx.resolve(keyname, OPENPGPKEY)
	if status == 0 and result.havedata:
		if not result.secure:
			if not insecure_ok:
				# The data is insecure and a secure lookup was requested
				sys.exit("Error: query data is not secured by DNSSEC - use --insecure to override")
			else:
				print >> sys.stderr, 'Warning: query data was not secured by DNSSEC.'
		# If we are here the data was either secure or insecure data is accepted
		return result.data.raw
	else:
		sys.exit('Unsuccesful lookup or no data returned for OPENPGPKEY (rrtype 61)')

if __name__ == '__main__':
	import argparse
	# create the parser
	parser = argparse.ArgumentParser(description='Create and verify OPENPGPKEY records.', epilog='For bugs. see paul@nohats.ca')

	parser.add_argument('--verify','-v', action='store_true', help='Verify an OPENPGPKEY record, exit 0 when all records are matched, exit 1 on error.')
	parser.add_argument('--fetch','-f', action='store_true', help='Fetch an OPENPGPKEY record, and show it in ascii armor on stdout')

	parser.add_argument('--create','-c', action='store_true', help='Create an OPENPGKEY record')
	parser.add_argument('--version', action='version', version='openpgpkey version: %s'%VERSION, help='show version and exit')

	parser.add_argument('--insecure', action='store_true', default=False, help='Allow use of non-dnssec secured answers')
	parser.add_argument('--resolvconf', action='store', default='', help='Use a recursive resolver listed in a resolv.conf file (default: /etc/resolv.conf)')
	parser.add_argument('--rootanchor', action='store', default='', help='Location of the unbound compatible DNSSEC root.anchor (default: /usr/share/dns/root.key)')
	parser.add_argument('email', metavar="email")

	parser.add_argument('--uid', action='store', default='', help='override the uid (email address) within the key')
	parser.add_argument('--keyid', action='store', default='', help='specify key by keyid')

	parser.add_argument('--debug', '-d', action='store_true', help='Print details plus the result of the validation')
	parser.add_argument('--quiet', '-q', action='store_true', help='Ignored for backwards compatibility')

	# default for now to generic - when more tools support OPENPGPKEY, switch the default to rfc
	parser.add_argument('--output', '-o', action='store', default='rfc', choices=['generic','rfc','both'], help='The type of output. Generic (RFC 3597, TYPE61), RFC (OPENPGPKEY) or both (default: %(default)s).')

	args = parser.parse_args()

	if args.verify and args.create:
		sys.exit("openpgpkey: must specify --create or --verify")

	if args.verify or args.fetch:
		# unbound setup, only for verify
		global ctx
		ctx = unbound.ub_ctx()
		resolvconf = "/etc/resolv.conf"
		rootanchor = None
		if args.rootanchor:
			if os.path.isfile(args.rootanchor):
				rootanchor = args.rootanchor
			else:
				print >> sys.stdout, 'openpgpkey: %s is not a file. Unable to use it as rootanchor' % args.rootanchor
				sys.exit(1)
		else:
			cauldron = ( "/usr/share/dns/root.key", "/var/lib/unbound/root.anchor", "/var/lib/unbound/root.key", "/etc/unbound/root.key" )
			for root in cauldron:
				if os.path.isfile(root):
					rootanchor=root
					break
		if rootanchor:
			try:
				ctx.add_ta_file(rootanchor)
			except:
				unbound.ub_ctx_trustedkeys(ctx,rootanchor)

		if args.resolvconf:
			if os.path.isfile(args.resolvconf):
				resolvconf = args.resolvconf
			else:
				print >> sys.stdout, 'openpgpkey: %s is not a file. Unable to use it as resolv.conf' % args.resolvconf
				sys.exit(1)
		ctx.resolvconf(resolvconf)

		openpgpkeys = getOPENPGPKEY(args.email, args.insecure)
		if len(openpgpkeys) == 0:
			print >> sys.stderr, 'openpgpkey: Received nothing?'
			sys.exit(1)
		fdir = tempfile.mkdtemp(".gpg","openpgpkey-","/tmp/")
		gpgnet = gnupg.GPG(gnupghome=fdir)
		gpgnet.decode_errors = 'ignore'

		for openpgpkey in openpgpkeys:
			import_result = gpgnet.import_keys(openpgpkey)
			if args.fetch:
				if args.keyid:
					pubkey = gpgnet.export_keys(args.keyid, minimal=True)
					if not pubkey:
						print >> sys.stderr, 'openpgpkey: Requested keyid not present in received OpenPGP data'
						sys.exit(1)
				if args.uid:
					pubkey = gpgnet.export_keys(args.uid, minimal=True)
					if not pubkey:
						print >> sys.stderr, 'openpgpkey: Requested uid not present in received OpenPGP data'
						for id in  gpgnet.list_keys()[0]['uids']:
							print >> sys.stderr, "# %s"%id
						sys.exit(1)
				if not args.uid and not args.keyid:
					pubkey = gpgnet.export_keys(args.email, minimal=True)
					print >> sys.stderr, 'openpgpkey: Received OpenPGP data does not contain a key with keyid %s'%args.email
					print >> sys.stderr, '(add --uid <uid> to override with any of the below received uids)'
					for id in  gpgnet.list_keys()[0]['uids']:
						print >> sys.stderr, "# %s"%id
					sys.exit(1)

				pubkey = pubkey.replace("Version:","Comment: %s key obtained from DNS\nVersion:"%args.email)
				if args.insecure:
					pubkey = pubkey.replace("Version:","Comment: NOT VALIDATED BY DNSSEC\nVersion:")
				else:
					pubkey = pubkey.replace("Version:","Comment: key transfer was protected by DNSSEC\nVersion:")
				print pubkey

		if args.fetch:
			sys.exit(0)

		received_keys = gpgnet.list_keys() 
		gpgdisk = gnupg.GPG()
		gpgdisk.decode_errors = 'ignore'
		disk_keys = gpgdisk.list_keys()
		for pkey in received_keys:
			if args.debug:
				print >> sys.stderr, "Received from DNS: Key-ID:%s Fingerprint:%s"%(pkey["keyid"], pkey["fingerprint"])
			found = False
			for dkey in disk_keys:
				if args.debug:
					print >> sys.stderr, "Local disk: Key-ID:%s Fingerprint:%s"%(dkey["keyid"], dkey["fingerprint"])
				if pkey["keyid"] == dkey["keyid"] and pkey["fingerprint"] == dkey["fingerprint"]:
					found = True
			if found == False:
				shutil.rmtree(fdir)
				sys.exit("Received key with keyid %s was not found"%pkey["keyid"])
			else:
				if args.debug:
					print >> sys.stderr, "Received key with keyid %s was found"%pkey["keyid"]
		print "All OPENPGPKEY records matched with content from the local keyring"
		shutil.rmtree(fdir)
		sys.exit(0)

	else: # we want to create
		gpgdisk = gnupg.GPG()
		gpgdisk.decode_errors = 'ignore'
		found = False
		# if we have the keyid, use that
		if args.keyid:
			ekey = gpgdisk.export_keys(args.keyid,minimal=True, secret=False, armor=False)
			if ekey:
				found = True
				createOPENPGPKEY(args.email, gpgdisk, args.keyid, args.output, args.debug)
		# else find key
		if not found:
			disk_keys = gpgdisk.list_keys()
			for pgpkey in disk_keys:
				for uid in pgpkey["uids"]:
					if "<%s>"%args.email in uid:
						if args.debug:
							print >> sys.stderr,  "Found matching KeyID: %s (%s) for %s"%(pgpkey["keyid"], pgpkey["fingerprint"], uid)
						ekey = gpgdisk.export_keys(pgpkey["keyid"],minimal=True, secret=False, armor=False)
						createOPENPGPKEY(args.email, gpgdisk, pgpkey["keyid"], args.output, args.debug)
						found = True
		# give up
		if not found:
			errt = "No key found for "
			if args.email:
				errt += "email address %s "%args.email
			if args.uid:
				errt += "or uid %s "%args.uid
			if args.keyid:
				errt += "or keyid %s "%args.keyid
			sys.exit(errt)