From owner-svn-src-user@FreeBSD.ORG Tue Oct 29 09:01:38 2013 Return-Path: Delivered-To: svn-src-user@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTP id 183228BC; Tue, 29 Oct 2013 09:01:38 +0000 (UTC) (envelope-from cperciva@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.freebsd.org (Postfix) with ESMTPS id EB0D2257A; Tue, 29 Oct 2013 09:01:37 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.7/8.14.7) with ESMTP id r9T91bfm084356; Tue, 29 Oct 2013 09:01:37 GMT (envelope-from cperciva@svn.freebsd.org) Received: (from cperciva@localhost) by svn.freebsd.org (8.14.7/8.14.5/Submit) id r9T91bHg084354; Tue, 29 Oct 2013 09:01:37 GMT (envelope-from cperciva@svn.freebsd.org) Message-Id: <201310290901.r9T91bHg084354@svn.freebsd.org> From: Colin Percival Date: Tue, 29 Oct 2013 09:01:37 +0000 (UTC) To: src-committers@freebsd.org, svn-src-user@freebsd.org Subject: svn commit: r257318 - user/cperciva/pkesh X-SVN-Group: user MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-user@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "SVN commit messages for the experimental " user" src tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 29 Oct 2013 09:01:38 -0000 Author: cperciva Date: Tue Oct 29 09:01:37 2013 New Revision: 257318 URL: http://svnweb.freebsd.org/changeset/base/257318 Log: Code for performing public-key encryption of a message. To be precise, the message is encrypted using AES256 in CBC mode; and the AES key, the CBC IV, and the SHA256 hash of the plaintext are then encrypted using RSA. In short, think GPG except without the "web of trust" and implemented as a shell script using only utilities in the FreeBSD base system. Added: user/cperciva/pkesh/ user/cperciva/pkesh/README user/cperciva/pkesh/pkesh.sh (contents, props changed) Added: user/cperciva/pkesh/README ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ user/cperciva/pkesh/README Tue Oct 29 09:01:37 2013 (r257318) @@ -0,0 +1,33 @@ +pkesh -- Public Key Encryption SHell script +=========================================== + +Usage +----- +# pkesh gen priv.key pub.key + Generates a public/private key pair. +# pkesh enc pub.key in out + Encrypts "in" using the public key, writing to "out". +# pkesh dec priv.key in out + Decrypts "in" using the private key, writing to "out". + +Note: Temporary working space is used under $TMP (or /tmp); if this is not +a memory filesystem then keys or data may be leaked to permanent storage. +There must be sufficient temporary space to hold the entire message. + +Encrypted format +---------------- +Encrypted Message = base64([Encrypted Header][Encrypted Data]) +Encrypted Header = RSA2048-OAEP([Header]) +Header = [AES256-CBC Key][AES256-CBC IV][Hash] +AES256-CBC Key = 256 bits (random) +AES256-CBC IV = 128 bits (random) +Hash = SHA256(Encrypted Data) +Encrypted Data = AES256-CBC(Data) +Data = arbitrary length input "in" + +Requirements +------------ + +openssl +POSIX utilities: sh, cat, cmp, dd, od, rm, tr, wc +non-POSIX but standard UNIX: mktemp Added: user/cperciva/pkesh/pkesh.sh ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ user/cperciva/pkesh/pkesh.sh Tue Oct 29 09:01:37 2013 (r257318) @@ -0,0 +1,116 @@ +#!/bin/sh -e + +usage () { + echo "usage: pkesh gen priv.key pub.key" + echo "usage: pkesh enc pub.key in out" + echo "usage: pkesh dec priv.key in out" + exit 1 +} + +# gen priv.key pub.key +gen () { + # Generate the key + openssl genrsa -out $D/rsakey -f4 2048 2>/dev/null + + # Write out private and public parts + cat $D/rsakey > $1 + openssl rsa -in $D/rsakey -pubout > $2 2>/dev/null +} + +# enc pub.key in out +enc () { + # Generate a random 256-bit AES key + openssl rand 32 > $D/aeskey + + # Generate a random 128-bit IV + openssl rand 16 > $D/aesIV + + # Generate the encrypted data + KEY=`od -An -v -t x1 < $D/aeskey | tr -Cd '0-9a-fA-F'` + IV=`od -An -v -t x1 < $D/aesIV | tr -Cd '0-9a-fA-F'` + openssl enc -aes-256-cbc -K $KEY -iv $IV < $2 > $D/encdata + + # Compute the SHA256 hash of the encrypted data + openssl dgst -sha256 -binary $D/encdata > $D/hash + + # Generate the header + cat $D/aeskey $D/aesIV $D/hash > $D/header + + # Generate the encrypted header + openssl rsautl -inkey $1 -pubin -encrypt -oaep \ + < $D/header > $D/encheader + + # Generate the entire encrypted message + cat $D/encheader $D/encdata | openssl enc -base64 > $3 +} + +# dec priv.key in out +dec () { + # Base-64 decode the encrypted message + openssl enc -d -base64 < $2 > $D/encmessage + + # Make sure the message is long enough + if [ `wc -c < $D/encmessage` -lt 256 ]; then + echo "Message is corrupt or truncated" >/dev/stderr + exit 1 + fi + + # Decrypt the header + dd if=$D/encmessage bs=256 count=1 of=$D/encheader 2>/dev/null + openssl rsautl -inkey $1 -decrypt -oaep < $D/encheader > $D/header + + # Make sure the header is the right size + if [ `wc -c < $D/header` -ne 80 ]; then + echo "Message is corrupt" >/dev/stderr + exit 1 + fi + + # Split header into components + dd if=$D/header bs=1 count=32 of=$D/aeskey 2>/dev/null + dd if=$D/header bs=1 skip=32 count=16 of=$D/aesIV 2>/dev/null + dd if=$D/header bs=1 skip=48 count=32 of=$D/hash 2>/dev/null + + # Verify the encrypted data hash + dd if=$D/encmessage bs=256 skip=1 2>/dev/null | + openssl dgst -sha256 -binary > $D/encmessage.hash + if ! cmp -s $D/hash $D/encmessage.hash; then + echo "Message is corrupt or truncated" >/dev/stderr + exit 1 + fi + + # Decrypt the message + KEY=`od -An -v -t x1 < $D/aeskey | tr -Cd '0-9a-fA-F'` + IV=`od -An -v -t x1 < $D/aesIV | tr -Cd '0-9a-fA-F'` + dd if=$D/encmessage bs=256 skip=1 2>/dev/null | + openssl enc -d -aes-256-cbc -K $KEY -iv $IV > $3 +} + +# Get operation type +if [ $# -lt 1 ]; then + usage +fi +OP=$1 +shift + +# Check operation type and number of operands +case $OP in +gen) + if [ $# -ne 2 ]; then + usage + fi + ;; +enc|dec) + if [ $# -ne 3 ]; then + usage + fi + ;; +*) + usage +esac + +# Create temporary working directory +D=`mktemp -d "${TMP:-/tmp}/pkesh.XXXXXX"` +trap 'rm -r "$D"' EXIT + +# Perform the operation +$OP "$@"