diff -r 9435b9008a58 -r 934f9131337b releasing/blocks/framework/src/Blocks/gpg.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/releasing/blocks/framework/src/Blocks/gpg.py Thu Sep 02 15:02:14 2010 +0800 @@ -0,0 +1,132 @@ +# +# Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +# All rights reserved. +# This component and the accompanying materials are made available +# under the terms of "Eclipse Public License v1.0" +# which accompanies this distribution, and is available +# at the URL "http://www.eclipse.org/legal/epl-v10.html". +# +# Initial Contributors: +# Nokia Corporation - initial contribution. +# +# Contributors: +# +# Description: +# Wrapper for gpg +# + +''' gpg command wrapper ''' + +import sys +from subprocess import Popen, PIPE +from collections import namedtuple +import logging + +_GNUPGPREFIX = "[GNUPG:] " +_GNUPGGOODSIG = "GOODSIG" +_GNUPGBADSIG = "BADSIG" +_GNUPGNOPUBKEY = "NO_PUBKEY" +_GNUPGKEYEXPIRED = "KEYEXPIRED" +_GNUPGREVKEYSIG = "REVKEYSIG" + +class GpgStatusCode(object): + VERIFIED, BADSIG, NO_PUBKEY, KEYEXPIRED, REVKEYSIG = range(5) + +VerifyInfo = namedtuple("VerifyInfo", "name") +VerifyStatus = namedtuple("VerifyStatus", "code, info") + +#define GNUPGVALIDSIG "[GNUPG:] VALIDSIG" +#define GNUPGNODATA "[GNUPG:] NODATA" + +class GpgError(Exception): + """ Gpg exited with error """ + def __init__(self, errorcode, output): + Exception.__init__(self, "Gpg failed with error code %s" % errorcode) + self.errorcode = errorcode + self.output = output.strip() + +def sign(sourcePath, outputPath, homedir=None, batch=False, passfile=None): + ''' + Create a gpg signature of a file. + + sign() has two modes: file and pipe. File: Specify sourcePath and outputPath + as strings to create signature of file sourcePath in file outputPath. Pipe: + Specify sourcePath as readable object and outputPath as None. The signature + is the return value. + + Use a combination of batch, homedir and passfile to eliminate the need for + interaction. + + File mode:: + gpg.sign("/my/file", "/my/file.gpg") + + Pipe mode without interaction:: + f = open("/my/file", "rb") + key = gpg.sign(f, None, "/my/passwordless/keydir", True) + f.close() + + @param sourcePath: Path of the file to sign, or pipe to read the file from + @type sourcePath: String or file-like object + @param outputPath: Path to write signature to, or None in pipe mode + @type outputPath: String or None + @param homedir: Directory to read keyfile from + @type homedir: String + @param batch: Whether to use I{--batch} with gpg command + @type batch: Boolean + @param passfile: Optional passphrase file to use with the key + @type passfile: String + ''' + cmdstr = "gpg -abs" + if homedir: + cmdstr += ' --homedir "%s"' % homedir + if batch: + cmdstr += ' --batch' + pStdin = None + else: + pStdin = sys.stdin + if passfile: + cmdstr += ' --passphrase-file "%s"' % passfile + + if isinstance(outputPath, basestring) and isinstance(sourcePath, basestring): + cmdstr += ' -o "%s" "%s"' % (outputPath, sourcePath) + p = Popen(cmdstr, shell=True, stdin=pStdin, stdout=PIPE, stderr=PIPE) + else: + assert (sourcePath and hasattr(sourcePath, "read")), "sourcePath not file-like object!" + p = Popen(cmdstr, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE) + blockSize = 32*1024 + buf = sourcePath.read(blockSize) + while buf: + try: + p.stdin.write(buf) + except IOError: + break + buf = sourcePath.read(blockSize) + (stdoutput, stderror) = p.communicate() + if stderror is None: + stderror = "" + if p.returncode != 0: + raise GpgError(p.returncode, stderror) + return stdoutput + +def verify(signFilePath, signedFilePath, homedir=None): + cmdstr = ('gpgv --keyring pubring.gpg --ignore-time-conflict --status-fd 2 %s "%s" "%s"' % + ('--homedir "%s"' % homedir if homedir else "", signFilePath, signedFilePath)) + logging.debug("GPG running: %s", cmdstr) + p = Popen(cmdstr, shell=True, stdin=sys.stdin, stdout=PIPE, stderr=PIPE) + (stdoutput, stderror) = p.communicate() + logging.debug("GPG stdout: %s", stdoutput) + logging.debug("GPG stderror (status): %s", stderror) + for line in stderror.splitlines(): + if line.startswith(_GNUPGPREFIX): + line = line.replace(_GNUPGPREFIX, "", 1) + (statusString, _, info) = line.partition(" ") + status = {_GNUPGGOODSIG: GpgStatusCode.VERIFIED, + _GNUPGBADSIG: GpgStatusCode.BADSIG, + _GNUPGNOPUBKEY: GpgStatusCode.NO_PUBKEY, + _GNUPGKEYEXPIRED: GpgStatusCode.KEYEXPIRED, + _GNUPGREVKEYSIG: GpgStatusCode.REVKEYSIG}.get(statusString) + if status is not None: + name = info.partition(" ")[2] + return VerifyStatus(status, VerifyInfo(name)) + if p.returncode != 0: + raise GpgError(p.returncode, stdoutput) \ No newline at end of file