diff --git a/tools/half-sign.c b/tools/half-sign.c new file mode 100644 index 000000000..454201799 --- /dev/null +++ b/tools/half-sign.c @@ -0,0 +1,120 @@ +#include +#include +#include +#include +#include +#include +#include + +// This program can be used to perform RSA public key signatures given only +// the hash of the file to be signed as input. + +// Sign with SHA1 +#define HASH_SIZE 20 + +void usage() { + printf("half-sign [binary hash file]\n"); + printf("\n"); + printf(" Computes and prints a binary RSA signature over data given the SHA1 hash of\n"); + printf(" the data as input.\n"); + printf("\n"); + printf(" should be PEM encoded.\n"); + printf("\n"); + printf(" The input SHA1 hash should be %d bytes in length. If no binary hash file is\n", HASH_SIZE); + printf(" specified, it will be read from stdin.\n"); + exit(1); +} + +void sign_hashed_data(EVP_PKEY *signing_key, unsigned char *md, size_t mdlen) { + // cribbed from the openssl EVP_PKEY_sign man page + EVP_PKEY_CTX *ctx; + unsigned char *sig; + size_t siglen; + + /* NB: assumes signing_key, md and mdlen are already set up + * and that signing_key is an RSA private key + */ + ctx = EVP_PKEY_CTX_new(signing_key, NULL); + if ((!ctx) + || (EVP_PKEY_sign_init(ctx) <= 0) + || (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0) + || (EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha1()) <= 0)) { + fprintf(stderr, "Failure establishing ctx for signature\n"); + exit(1); + } + + /* Determine buffer length */ + if (EVP_PKEY_sign(ctx, NULL, &siglen, md, mdlen) <= 0) { + fprintf(stderr, "Unable to determine buffer length for signature\n"); + exit(1); + } + + sig = OPENSSL_malloc(siglen); + + if (!sig) { + fprintf(stderr, "Malloc failed\n"); + exit(1); + } + + if (EVP_PKEY_sign(ctx, sig, &siglen, md, mdlen) <= 0) { + fprintf(stderr, "Signature error\n"); + exit(1); + } + + /* Signature is siglen bytes written to buffer sig */ + fwrite(sig, siglen, 1, stdout); +} + +EVP_PKEY *read_private_key(char *filename) { + FILE *keyfile; + EVP_PKEY *privkey; + keyfile = fopen(filename, "r"); + if (!keyfile) { + fprintf(stderr, "Failed to open private key.pem file %s\n", filename); + exit(1); + } + privkey = PEM_read_PrivateKey(keyfile, NULL, NULL, NULL); + if (!privkey) { + fprintf(stderr, "Failed to read PEM private key from %s\n", filename); + exit(1); + } + if (EVP_PKEY_type(privkey->type) != EVP_PKEY_RSA) { + fprintf(stderr, "%s was a non-RSA key\n", filename); + exit(1); + } + return privkey; +} + +int main(int argc, char *argv[]) { + FILE *input; + unsigned char *buffer; + int test; + EVP_PKEY *privkey; + if (argc > 3 || argc < 2) + usage(); + if (argc < 3 || strcmp(argv[2],"-") == 0) + input = stdin; + else { + input = fopen(argv[2], "r"); + if (!input) usage(); + } + privkey = read_private_key(argv[1]); + buffer = malloc(HASH_SIZE); + if (!buffer) { + fprintf(stderr, "Argh, malloc failed\n"); + exit(1); + } + if (fread(buffer, HASH_SIZE, 1, input) != 1) { + perror("half-sign: Failed to read SHA1 from input\n"); + exit(1); + } + + test = fgetc(input); + if (test != EOF && test != '\n') { + fprintf(stderr,"Error, more than %d bytes fed to half-sign\n", HASH_SIZE); + fprintf(stderr,"Last byte was :%d\n" , (int) test); + exit(1); + } + sign_hashed_data(privkey, buffer, HASH_SIZE); + return 0; +} diff --git a/tools/dev-release.sh b/tools/release.sh similarity index 51% rename from tools/dev-release.sh rename to tools/release.sh index d8c720559..eeabfd4a3 100755 --- a/tools/dev-release.sh +++ b/tools/release.sh @@ -1,13 +1,43 @@ -#!/bin/sh -xe +#!/bin/bash -xe # Release dev packages to PyPI +Usage() { + echo Usage: + echo "$0 [ --production ]" + exit 1 +} + +if [ "`dirname $0`" != "tools" ] ; then + echo Please run this script from the repo root + exit 1 +fi + +CheckVersion() { + # Args: + if ! echo "$2" | grep -q -e '[0-9]\+.[0-9]\+.[0-9]\+' ; then + echo "$1 doesn't look like 1.2.3" + exit 1 + fi +} + +if [ "$1" = "--production" ] ; then + version="$2" + CheckVersion Version "$version" + echo Releasing production version "$version"... + nextversion="$3" + CheckVersion "Next version" "$nextversion" + RELEASE_BRANCH="candidate-$version" +else + version=`grep "__version__" letsencrypt/__init__.py | cut -d\' -f2 | sed s/\.dev0//` + version="$version.dev$(date +%Y%m%d)1" + RELEASE_BRANCH="dev-release" + echo Releasing developer version "$version"... +fi + +RELEASE_GPG_KEY=${RELEASE_GPG_KEY:-A2CFB51FA275A7286234E7B24D17C995CD9775F2} # Needed to fix problems with git signatures and pinentry export GPG_TTY=$(tty) -version="0.0.0.dev$(date +%Y%m%d)" -DEV_RELEASE_BRANCH="dev-release" -RELEASE_GPG_KEY=A2CFB51FA275A7286234E7B24D17C995CD9775F2 - # port for a local Python Package Index (used in testing) PORT=${PORT:-1234} @@ -36,21 +66,29 @@ pip install -U wheel # setup.py bdist_wheel # from current env when creating a child env pip install -U virtualenv -root="$(mktemp -d -t le.$version.XXX)" +root_without_le="$version.$$" +root="./releases/le.$root_without_le" + echo "Cloning into fresh copy at $root" # clean repo = no artificats git clone . $root git rev-parse HEAD cd $root -git branch -f "$DEV_RELEASE_BRANCH" -git checkout "$DEV_RELEASE_BRANCH" +if [ "$RELEASE_BRANCH" != "candidate-$version" ] ; then + git branch -f "$RELEASE_BRANCH" +fi +git checkout "$RELEASE_BRANCH" -for pkg_dir in $SUBPKGS -do - sed -i $x "s/^version.*/version = '$version'/" $pkg_dir/setup.py -done -sed -i "s/^__version.*/__version__ = '$version'/" letsencrypt/__init__.py +SetVersion() { + ver="$1" + for pkg_dir in $SUBPKGS + do + sed -i "s/^version.*/version = '$ver'/" $pkg_dir/setup.py + done + sed -i "s/^__version.*/__version__ = '$ver'/" letsencrypt/__init__.py -git add -p # interactive user input + git add -p $SUBPKGS # interactive user input +} +SetVersion "$version" git commit --gpg-sign="$RELEASE_GPG_KEY" -m "Release $version" git tag --local-user "$RELEASE_GPG_KEY" \ --sign --message "Release $version" "$tag" @@ -68,7 +106,7 @@ do echo "Signing ($pkg_dir)" for x in dist/*.tar.gz dist/*.whl do - gpg2 --detach-sign --armor --sign $x + gpg -u "$RELEASE_GPG_KEY" --detach-sign --armor --sign $x done cd - @@ -97,16 +135,44 @@ pip install \ letsencrypt $SUBPKGS # stop local PyPI kill $! +cd ~- # freeze before installing anything else, so that we know end-user KGS # make sure "twine upload" doesn't catch "kgs" +if [ -d ../kgs ] ; then + echo Deleting old kgs... + rm -rf ../kgs +fi mkdir ../kgs kgs="../kgs/$version" pip freeze | tee $kgs pip install nose -nosetests letsencrypt $subpkgs_modules +for module in letsencrypt $subpkgs_modules ; do + echo testing $module + nosetests $module +done +deactivate + +cd .. +echo Now in $PWD +name=${root_without_le%.*} +ext="${root_without_le##*.}" +rev="$(git rev-parse --short HEAD)" +echo tar cJvf $name.$rev.tar.xz $name.$rev +echo gpg -U $RELEASE_GPG_KEY --detach-sign --armor $name.$rev.tar.xz +cd ~- echo "New root: $root" echo "KGS is at $root/kgs" +echo "Test commands (in the letstest repo):" +echo 'python multitester.py targets.yaml $AWS_KEY $USERNAME scripts/test_leauto_upgrades.sh --alt_pip $YOUR_PIP_REPO --branch public-beta' +echo 'python multitester.py targets.yaml $AWK_KEY $USERNAME scripts/test_letsencrypt_auto_certonly_standalone.sh --branch candidate-0.1.1' +echo 'python multitester.py --saveinstances targets.yaml $AWS_KEY $USERNAME scripts/test_apache2.sh' echo "In order to upload packages run the following command:" echo twine upload "$root/dist.$version/*/*" + +if [ "$RELEASE_BRANCH" = candidate-"$version" ] ; then + SetVersion "$nextversion".dev0 + git diff + git commit -m "Bump version to $nextversion" +fi