├── checks ├── corrupt-raw.bin ├── papertest-100.pub ├── papertest-100.sec ├── papertest-ecc.pub ├── papertest-ecc.sec ├── papertest-eddsa.pub ├── papertest-eddsa.sec ├── papertest-rsa.pub ├── papertest-rsa.sec ├── papertest-dsa100.pub ├── papertest-dsa100.sec ├── papertest-dsaelg.pub ├── papertest-dsaelg.sec ├── bad-primary.sh ├── bad-subkey.sh ├── bad-crc-base16.sh ├── bad-crc-raw.sh ├── Makefile.am ├── roundtrip.sh ├── roundtrip-raw.sh └── corrupt-base16.txt ├── AUTHORS ├── README.WIN32 ├── .gitignore ├── Makefile.am ├── extract.h ├── restore.h ├── parse.h ├── packets.h ├── paperkey.spec.in ├── output.h ├── configure.ac ├── packets.c ├── gl └── m4 │ └── gnulib-cache.m4 ├── extract.c ├── NEWS ├── restore.c ├── paperkey.c ├── paperkey.1 ├── ChangeLog ├── output.c ├── README.md ├── parse.c └── COPYING /checks/corrupt-raw.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmshaw/paperkey/HEAD/checks/corrupt-raw.bin -------------------------------------------------------------------------------- /checks/papertest-100.pub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmshaw/paperkey/HEAD/checks/papertest-100.pub -------------------------------------------------------------------------------- /checks/papertest-100.sec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmshaw/paperkey/HEAD/checks/papertest-100.sec -------------------------------------------------------------------------------- /checks/papertest-ecc.pub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmshaw/paperkey/HEAD/checks/papertest-ecc.pub -------------------------------------------------------------------------------- /checks/papertest-ecc.sec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmshaw/paperkey/HEAD/checks/papertest-ecc.sec -------------------------------------------------------------------------------- /checks/papertest-eddsa.pub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmshaw/paperkey/HEAD/checks/papertest-eddsa.pub -------------------------------------------------------------------------------- /checks/papertest-eddsa.sec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmshaw/paperkey/HEAD/checks/papertest-eddsa.sec -------------------------------------------------------------------------------- /checks/papertest-rsa.pub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmshaw/paperkey/HEAD/checks/papertest-rsa.pub -------------------------------------------------------------------------------- /checks/papertest-rsa.sec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmshaw/paperkey/HEAD/checks/papertest-rsa.sec -------------------------------------------------------------------------------- /checks/papertest-dsa100.pub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmshaw/paperkey/HEAD/checks/papertest-dsa100.pub -------------------------------------------------------------------------------- /checks/papertest-dsa100.sec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmshaw/paperkey/HEAD/checks/papertest-dsa100.sec -------------------------------------------------------------------------------- /checks/papertest-dsaelg.pub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmshaw/paperkey/HEAD/checks/papertest-dsaelg.pub -------------------------------------------------------------------------------- /checks/papertest-dsaelg.sec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmshaw/paperkey/HEAD/checks/papertest-dsaelg.sec -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | David Shaw (program) 2 | Peter Palfrader (man page) 3 | -------------------------------------------------------------------------------- /README.WIN32: -------------------------------------------------------------------------------- 1 | This is the Win32 release of Paperkey. It is identical to the regular 2 | POSIX release, with the addition of this README.WIN32 file, 3 | instructions for use in paperkey.txt and a pre-compiled paperkey.exe 4 | binary. 5 | -------------------------------------------------------------------------------- /checks/bad-primary.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Feed paperkey an algo-100 primary. It should fail. 4 | 5 | ../paperkey --secret-key ${srcdir}/papertest-100.sec >/dev/null 2>&1 6 | if test $? -ne 1 ; then 7 | exit 1 8 | fi 9 | 10 | exit 0 11 | -------------------------------------------------------------------------------- /checks/bad-subkey.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Feed paperkey a DSA primary and an algo-100 subkey. It should fail. 4 | 5 | ../paperkey --secret-key ${srcdir}/papertest-dsa100.sec >/dev/null 2>&1 6 | if test $? -ne 1 ; then 7 | exit 1 8 | fi 9 | 10 | exit 0 11 | -------------------------------------------------------------------------------- /checks/bad-crc-base16.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Feed paperkey a text file with corrupt CRC. It should fail. 4 | 5 | ../paperkey --secrets ${srcdir}/corrupt-base16.txt --pubring ${srcdir}/papertest.pub >/dev/null 2>&1 6 | if test $? -ne 1 ; then 7 | exit 1 8 | fi 9 | 10 | exit 0 11 | -------------------------------------------------------------------------------- /checks/bad-crc-raw.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Feed paperkey a binary/raw file with corrupt CRC. It should fail. 4 | 5 | ../paperkey --secrets ${srcdir}/corrupt-raw.bin --pubring ${srcdir}/papertest.pub >/dev/null 2>&1 6 | if test $? -ne 1 ; then 7 | exit 1 8 | fi 9 | 10 | exit 0 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .deps 2 | INSTALL 3 | **/Makefile 4 | **/Makefile.in 5 | aclocal.m4 6 | autom4te.cache 7 | checks/*.log 8 | checks/*.trs 9 | checks/papertest-*.bin 10 | checks/papertest-*.txt 11 | checks/regen-*.pgp 12 | config.guess 13 | config.h 14 | config.h.in 15 | config.log 16 | config.status 17 | config.sub 18 | configure 19 | depcomp 20 | gl/* 21 | install-sh 22 | missing 23 | paperkey 24 | paperkey.spec 25 | stamp-h1 26 | test-driver 27 | -------------------------------------------------------------------------------- /checks/Makefile.am: -------------------------------------------------------------------------------- 1 | TESTS=roundtrip.sh roundtrip-raw.sh bad-crc-base16.sh bad-crc-raw.sh bad-primary.sh bad-subkey.sh 2 | EXTRA_DIST=$(TESTS) papertest-rsa.pub papertest-rsa.sec papertest-dsaelg.pub papertest-dsaelg.sec papertest-ecc.pub papertest-ecc.sec papertest-eddsa.pub papertest-eddsa.sec papertest-100.pub papertest-100.sec papertest-dsa100.pub papertest-dsa100.sec corrupt-base16.txt corrupt-raw.bin 3 | CLEANFILES=papertest-*.bin papertest-*.txt regen-*.pgp 4 | -------------------------------------------------------------------------------- /checks/roundtrip.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Roundtrip base16 test - go from a secret key to base16 then back to 4 | # a secret key. 5 | 6 | for type in rsa dsaelg ecc eddsa ; do 7 | ../paperkey --secret-key ${srcdir}/papertest-${type}.sec --output papertest-${type}.txt || exit 1 8 | ../paperkey --secrets papertest-${type}.txt --pubring ${srcdir}/papertest-${type}.pub --output regen-${type}.pgp || exit 1 9 | cmp ./regen-${type}.pgp ${srcdir}/papertest-${type}.sec || exit 1 10 | /bin/echo -n "$type " 11 | done 12 | -------------------------------------------------------------------------------- /checks/roundtrip-raw.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Roundtrip binary/raw test - go from a secret key to binary/raw then 4 | # back to a secret key. 5 | 6 | for type in rsa dsaelg ecc eddsa ; do 7 | ../paperkey --secret-key ${srcdir}/papertest-${type}.sec --output-type raw --output papertest-${type}.bin || exit 1 8 | ../paperkey --secrets papertest-${type}.bin --pubring ${srcdir}/papertest-${type}.pub --output regen-raw-${type}.pgp || exit 1 9 | cmp ./regen-raw-${type}.pgp ${srcdir}/papertest-${type}.sec || exit 1 10 | /bin/echo -n "$type " 11 | done 12 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | ACLOCAL_AMFLAGS=-I gl/m4 2 | EXTRA_DIST=paperkey.spec $(man_MANS) 3 | SUBDIRS=gl . 4 | 5 | AM_CPPFLAGS=-I$(top_srcdir)/gl 6 | LDADD=gl/libgl.a 7 | 8 | man_MANS=paperkey.1 9 | 10 | bin_PROGRAMS=paperkey 11 | 12 | paperkey_SOURCES=paperkey.c packets.c packets.h parse.c parse.h extract.c extract.h restore.c restore.h output.c output.h 13 | 14 | if USING_GCC 15 | AM_CPPFLAGS+=-Wall -W -pedantic 16 | endif 17 | 18 | # Only do the self test if we're not cross compiling 19 | if !CROSS_COMPILING 20 | SUBDIRS+=checks 21 | endif 22 | 23 | if MINGW32_BUILD 24 | CLEANFILES=paperkey.txt 25 | EXTRA_DIST+=README.WIN32 paperkey.txt paperkey.exe 26 | paperkey.txt: paperkey.1 27 | nroff -u0 -man paperkey.1 | col -b > paperkey.txt 28 | endif 29 | -------------------------------------------------------------------------------- /extract.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007 David Shaw 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | #ifndef _EXTRACT_H_ 20 | #define _EXTRACT_H_ 21 | 22 | int extract(FILE *input,const char *outname,enum data_type output_type); 23 | 24 | #endif /* !_EXTRACT_H_ */ 25 | -------------------------------------------------------------------------------- /restore.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007 David Shaw 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | #ifndef _RESTORE_H_ 20 | #define _RESTORE_H_ 21 | 22 | int restore(FILE *pubring,FILE *secrets, 23 | enum data_type input_type,const char *outname); 24 | 25 | #endif /* !_RESTORE_H_ */ 26 | 27 | -------------------------------------------------------------------------------- /checks/corrupt-base16.txt: -------------------------------------------------------------------------------- 1 | # Secret portions of key 23480A56631955A989125E0C205AE9891323F931 2 | # Base 16 data extracted Sat Sep 22 23:52:40 2007 3 | # Created with paperkey 0.7-devel by David Shaw 4 | 1: 00 04 23 48 0A 56 63 19 55 A9 89 12 5E 0C 20 5A E9 89 13 23 F9 31 4FD803 5 | 2: 00 3F FE 03 03 02 56 AC A0 3D F2 14 48 D2 60 22 90 E7 0A 58 94 51 E9F446 6 | 3: F7 3D 5B 2A 4D 9C 26 B9 C1 AF 27 34 D3 D0 95 FE 69 9A C8 7D A4 E8 F278AF 7 | 4: 00 4E 9A 52 11 E9 C3 68 E4 BC E7 0E B2 15 D8 47 8A 6A 19 95 A6 04 F4D931 8 | 5: E9 C2 FC 3F 48 D9 04 A9 E0 26 A1 38 62 D6 70 2D BD C5 DF C4 00 55 004333 9 | 6: FE 03 03 02 56 AC A0 3D F2 14 48 D2 60 91 84 44 F2 DB A1 1A E8 0E 4E71CE 10 | 7: D4 72 82 18 FB E6 B6 85 6A 32 F6 15 E9 89 1C 7F 55 DD D5 45 EE 11 000000 11 | 8: E7 DE 44 4C 2B 5B EB 2C 30 91 1E 1F F0 03 6A 2C 30 AD 55 90 6C D0 3C5ED5 12 | 9: 9F 39 7C E7 53 06 F2 BE F7 AE 1B A1 DE A2 1A 58 8A C5 2C 04 B7 F5 2F9B32 13 | 10: F3 47 68 F5 1E EF 64 B3 FA 3F 57 FE F4 5C BC 0B FC F4 00 3F FE 03 B4EE51 14 | 11: 03 02 33 E6 5A 12 FA 15 D4 24 60 91 E7 D2 F8 74 2D DB 04 38 78 DC 4C2FAC 15 | 12: 6B C8 A2 AC 25 6D 5D 6B DE E5 01 F8 12 19 D5 DB 33 16 2E A5 C2 AA 67FF61 16 | 13: 9E AA 72 AB 8B 3B 4D D1 9D B5 6C EF E1 31 BF 99 19 F954A9 17 | 14: 141A60 18 | -------------------------------------------------------------------------------- /parse.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007 David Shaw 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | #ifndef _PARSE_H_ 20 | #define _PARSE_H_ 21 | 22 | struct packet *parse(FILE *input,unsigned char want,unsigned char stop); 23 | int calculate_fingerprint(struct packet *packet,size_t public_len, 24 | unsigned char fingerprint[20]); 25 | ssize_t extract_secrets(struct packet *packet); 26 | struct packet *read_secrets_file(FILE *secrets,enum data_type input_type); 27 | 28 | #endif /* !_PARSE_H_ */ 29 | -------------------------------------------------------------------------------- /packets.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007 David Shaw 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | #ifndef _PACKETS_H_ 20 | #define _PACKETS_H_ 21 | 22 | #include 23 | 24 | struct packet 25 | { 26 | unsigned char type; 27 | unsigned char *buf; 28 | /* The length the data we've put into buf. */ 29 | size_t len; 30 | /* The length we've malloced for buf. */ 31 | size_t size; 32 | }; 33 | 34 | void *xrealloc(void *ptr,size_t size); 35 | #define xmalloc(_size) xrealloc(NULL,_size) 36 | struct packet *append_packet(struct packet *packet, 37 | unsigned char *buf,size_t len); 38 | void free_packet(struct packet *packet); 39 | 40 | #endif /* !_PACKETS_H_ */ 41 | -------------------------------------------------------------------------------- /paperkey.spec.in: -------------------------------------------------------------------------------- 1 | Name: paperkey 2 | Version: @PACKAGE_VERSION@ 3 | Release: 1 4 | Summary: An OpenPGP key archiver 5 | Group: Applications/Archiving 6 | License: GPLv2+ 7 | URL: http://www.jabberwocky.com/software/paperkey/ 8 | Source0: http://www.jabberwocky.com/software/%{name}/%{name}-%{version}.tar.gz 9 | Source1: http://www.jabberwocky.com/software/%{name}/%{name}-%{version}.tar.gz.sig 10 | BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) 11 | 12 | %description 13 | A reasonable way to achieve a long term backup of OpenPGP (PGP, GnuPG, 14 | etc) keys is to print them out on paper. Paper and ink have amazingly 15 | long retention qualities - far longer than the magnetic or optical 16 | means that are generally used to back up computer data. A paper 17 | backup isn't a replacement for the usual machine readable (tape, CD-R, 18 | DVD-R, etc) backups, but rather as an if-all-else-fails method of 19 | restoring a key. 20 | 21 | %prep 22 | %setup -q 23 | 24 | %build 25 | %configure 26 | make %{?_smp_mflags} check 27 | 28 | %install 29 | rm -rf "$RPM_BUILD_ROOT" 30 | make install DESTDIR="$RPM_BUILD_ROOT" 31 | 32 | %clean 33 | rm -rf "$RPM_BUILD_ROOT" 34 | 35 | %files 36 | %{!?_licensedir:%global license %doc} 37 | %defattr(-,root,root,-) 38 | %doc AUTHORS ChangeLog README NEWS 39 | %license COPYING 40 | %{_bindir}/%{name} 41 | %{_mandir}/man1/%{name}.1.gz 42 | 43 | %changelog 44 | * Mon Sep 5 2016 David Shaw 45 | - Use %license for the COPYING file and run the self checks during build. 46 | 47 | * Fri Jan 16 2009 David Shaw 48 | - Borrow some RPM-fu from the Fedora RPM. 49 | -------------------------------------------------------------------------------- /output.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007, 2012, 2016 David Shaw 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | #ifndef _OUTPUT_H_ 20 | #define _OUTPUT_H_ 21 | 22 | #include 23 | 24 | enum data_type {AUTO,BASE16,RAW}; 25 | 26 | #define CRC24_INIT 0xB704CEL 27 | 28 | void do_crc24(unsigned long *crc,const unsigned char *buf,size_t len); 29 | void print_bytes(FILE *stream,const unsigned char *buf,size_t length); 30 | void output_file_format(FILE *stream,const char *prefix); 31 | int output_start(const char *name,enum data_type type, 32 | unsigned char fingerprint[20]); 33 | ssize_t output_bytes(const unsigned char *buf,size_t length); 34 | #define output_packet(_packet) output_bytes((_packet)->buf,(_packet)->len) 35 | ssize_t output_length16(size_t length); 36 | ssize_t output_openpgp_header(unsigned char tag,size_t length); 37 | void output_finish(void); 38 | void set_binary_mode(FILE *stream); 39 | 40 | #endif /* !_OUTPUT_H_ */ 41 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # -*- Autoconf -*- 2 | # Process this file with autoconf to produce a configure script. 3 | 4 | AC_PREREQ(2.64) 5 | AC_INIT([paperkey],[1.7-devel],[dshaw@jabberwocky.com]) 6 | m4_define(_COPYRIGHT_STRING, 7 | [[Copyright (C) 2007-2009, 2012, 2013, 2016-2018, 2020, 2021 David Shaw ]]) 8 | AC_COPYRIGHT(_COPYRIGHT_STRING) 9 | AC_USE_SYSTEM_EXTENSIONS 10 | AC_CONFIG_SRCDIR([paperkey.c]) 11 | AC_CONFIG_HEADER([config.h]) 12 | AM_INIT_AUTOMAKE([std-options]) 13 | AC_CANONICAL_HOST 14 | AC_DEFINE(COPYRIGHT_STRING,"_COPYRIGHT_STRING",[The copyright string]) 15 | AM_CONDITIONAL(CROSS_COMPILING, test "$cross_compiling" = "yes") 16 | AM_CONDITIONAL(MINGW32_BUILD, test "$host_os" = "mingw32") 17 | 18 | # This is so "make dist" when cross compiling has a "-win32" at the 19 | # end of the name. 20 | if test "$host_os" = "mingw32" ; then 21 | VERSION="$VERSION-win32" 22 | fi 23 | 24 | # Checks for programs. 25 | AC_PROG_CC 26 | gl_EARLY 27 | AC_PROG_RANLIB 28 | 29 | # Checks for libraries. 30 | 31 | # Checks for header files. 32 | AC_HEADER_STDC 33 | AC_CHECK_HEADERS([stdlib.h string.h]) 34 | 35 | # Checks for typedefs, structures, and compiler characteristics. 36 | AC_C_CONST 37 | AC_TYPE_SIZE_T 38 | AC_TYPE_SSIZE_T 39 | 40 | # Checks for library functions. 41 | 42 | # Gnulib. 43 | gl_INIT 44 | 45 | AC_ARG_ENABLE(optimization, 46 | AS_HELP_STRING([--disable-optimization],[disable compiler optimization]), 47 | [if test $enableval = no ; then 48 | CFLAGS=`echo $CFLAGS | sed 's/-O[[0-9]]//'` 49 | fi]) 50 | 51 | AM_CONDITIONAL(USING_GCC, test "$GCC" = yes) 52 | 53 | AC_DEFINE_UNQUOTED(HOST_OS,"$host_os",[Build host OS]) 54 | 55 | AC_CONFIG_FILES([gl/Makefile Makefile checks/Makefile paperkey.spec]) 56 | AC_OUTPUT 57 | -------------------------------------------------------------------------------- /packets.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007 David Shaw 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "sha1.h" 24 | #include "output.h" 25 | #include "packets.h" 26 | 27 | extern int verbose; 28 | 29 | void * 30 | xrealloc(void *ptr,size_t size) 31 | { 32 | ptr=realloc(ptr,size); 33 | if(!ptr) 34 | { 35 | fprintf(stderr,"Unable to allocate memory\n"); 36 | abort(); 37 | } 38 | 39 | return ptr; 40 | } 41 | 42 | struct packet * 43 | append_packet(struct packet *packet,unsigned char *buf,size_t len) 44 | { 45 | if(packet) 46 | { 47 | while(packet->size-packet->lensize+=100; 50 | packet->buf=xrealloc(packet->buf,packet->size); 51 | } 52 | 53 | memcpy(&packet->buf[packet->len],buf,len); 54 | packet->len+=len; 55 | } 56 | else 57 | { 58 | packet=xmalloc(sizeof(*packet)); 59 | packet->type=0; 60 | packet->buf=xmalloc(len); 61 | packet->len=len; 62 | packet->size=len; 63 | 64 | memcpy(packet->buf,buf,len); 65 | } 66 | 67 | return packet; 68 | } 69 | 70 | void 71 | free_packet(struct packet *packet) 72 | { 73 | if(packet) 74 | { 75 | free(packet->buf); 76 | free(packet); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /gl/m4/gnulib-cache.m4: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2002-2021 Free Software Foundation, Inc. 2 | # 3 | # This file is free software; you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation; either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This file is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this file. If not, see . 15 | # 16 | # As a special exception to the GNU General Public License, 17 | # this file may be distributed as part of a program that 18 | # contains a configuration script generated by Autoconf, under 19 | # the same distribution terms as the rest of that program. 20 | # 21 | # Generated by gnulib-tool. 22 | # 23 | # This file represents the specification of how gnulib-tool is used. 24 | # It acts as a cache: It is written and read by gnulib-tool. 25 | # In projects that use version control, this file is meant to be put under 26 | # version control, like the configure.ac and various Makefile.am files. 27 | 28 | 29 | # Specification in the form of a command-line invocation: 30 | # gnulib-tool --import \ 31 | # --lib=libgl \ 32 | # --source-base=gl \ 33 | # --m4-base=gl/m4 \ 34 | # --doc-base=doc \ 35 | # --tests-base=tests \ 36 | # --aux-dir=. \ 37 | # --no-conditional-dependencies \ 38 | # --no-libtool \ 39 | # --macro-prefix=gl \ 40 | # crypto/sha1 \ 41 | # getopt-gnu 42 | 43 | # Specification in the form of a few gnulib-tool.m4 macro invocations: 44 | gl_LOCAL_DIR([]) 45 | gl_MODULES([ 46 | crypto/sha1 47 | getopt-gnu 48 | ]) 49 | gl_AVOID([]) 50 | gl_SOURCE_BASE([gl]) 51 | gl_M4_BASE([gl/m4]) 52 | gl_PO_BASE([]) 53 | gl_DOC_BASE([doc]) 54 | gl_TESTS_BASE([tests]) 55 | gl_LIB([libgl]) 56 | gl_MAKEFILE_NAME([]) 57 | gl_MACRO_PREFIX([gl]) 58 | gl_PO_DOMAIN([]) 59 | gl_WITNESS_C_MACRO([]) 60 | -------------------------------------------------------------------------------- /extract.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007, 2017 David Shaw 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | #include 20 | #include 21 | #include "packets.h" 22 | #include "output.h" 23 | #include "parse.h" 24 | #include "extract.h" 25 | 26 | extern int verbose; 27 | 28 | int 29 | extract(FILE *input,const char *outname,enum data_type output_type) 30 | { 31 | struct packet *packet; 32 | int offset; 33 | unsigned char fingerprint[20]; 34 | unsigned char version=0; 35 | 36 | packet=parse(input,5,0); 37 | if(!packet) 38 | { 39 | fprintf(stderr,"Unable to find secret key packet\n"); 40 | return 1; 41 | } 42 | 43 | offset=extract_secrets(packet); 44 | if(offset==-1) 45 | return 1; 46 | 47 | if(verbose>1) 48 | fprintf(stderr,"Secret offset is %d\n",offset); 49 | 50 | calculate_fingerprint(packet,offset,fingerprint); 51 | 52 | if(verbose) 53 | { 54 | fprintf(stderr,"Primary key fingerprint: "); 55 | print_bytes(stderr,fingerprint,20); 56 | fprintf(stderr,"\n"); 57 | } 58 | 59 | output_start(outname,output_type,fingerprint); 60 | output_bytes(&version,1); 61 | output_bytes(packet->buf,1); 62 | output_bytes(fingerprint,20); 63 | output_length16(packet->len-offset); 64 | output_bytes(&packet->buf[offset],packet->len-offset); 65 | 66 | free_packet(packet); 67 | 68 | while((packet=parse(input,7,5))) 69 | { 70 | offset=extract_secrets(packet); 71 | if(offset==-1) 72 | return 1; 73 | 74 | if(verbose>1) 75 | fprintf(stderr,"Secret subkey offset is %d\n",offset); 76 | 77 | calculate_fingerprint(packet,offset,fingerprint); 78 | 79 | if(verbose) 80 | { 81 | fprintf(stderr,"Subkey fingerprint: "); 82 | print_bytes(stderr,fingerprint,20); 83 | fprintf(stderr,"\n"); 84 | } 85 | 86 | output_bytes(packet->buf,1); 87 | output_bytes(fingerprint,20); 88 | output_length16(packet->len-offset); 89 | output_bytes(&packet->buf[offset],packet->len-offset); 90 | 91 | free_packet(packet); 92 | } 93 | 94 | output_finish(); 95 | 96 | if(input==stdin) 97 | { 98 | /* Consume everything else on input */ 99 | while((fgetc(input)!=EOF)) 100 | ; 101 | } 102 | 103 | return 0; 104 | } 105 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | Noteworthy changes in version 1.7 (unreleased) 2 | ---------------------------------------------- 3 | 4 | 5 | Noteworthy changes in version 1.6 (2018-10-19) 6 | ---------------------------------------------- 7 | 8 | * Selftest updates to allow running tests in parallel via make -j 9 | (Debian bug 881792). Reported by Daniel Kahn Gillmor. 10 | 11 | 12 | Noteworthy changes in version 1.5 (2017-10-25) 13 | ---------------------------------------------- 14 | 15 | * Added support for OpenPGP EdDSA elliptic curve keys as specified 16 | in draft-koch-eddsa-for-openpgp-04. 17 | 18 | * Properly handle a secret key where the primary key is of a known 19 | type but a subkey is not of a known type. 20 | 21 | * Gnulib refresh. 22 | 23 | 24 | Noteworthy changes in version 1.4 (2016-09-05) 25 | ---------------------------------------------- 26 | 27 | * Fix binary vs text output mismatch when using "--output-type 28 | raw" on Windows platforms. Does not affect any non-Windows 29 | platforms. Found by Thierry Ambrus. 30 | 31 | * Gnulib refresh. 32 | 33 | 34 | Noteworthy changes in version 1.3 (2013-01-03) 35 | ---------------------------------------------- 36 | 37 | * Added support for OpenPGP elliptic curve keys (ECDH and ECDSA) 38 | as specified in RFC 6637. 39 | 40 | * Use minimal OpenPGP packet encoding. This does not affect 41 | functionality, but makes it more likely that a given key will 42 | match byte-for-byte the same key after running through paperkey. 43 | 44 | * Minor bug fixes. 45 | 46 | * Gnulib refresh. 47 | 48 | 49 | Noteworthy changes in version 1.2 (2009-10-14) 50 | ---------------------------------------------- 51 | 52 | * Minor documentation changes. 53 | 54 | * Gnulib refresh. 55 | 56 | * Generate output files with more restrictive permissions (go-rwx). 57 | 58 | 59 | Noteworthy changes in version 1.1 (2009-02-10) 60 | ---------------------------------------------- 61 | 62 | * Minor bug fix for Windows platforms. 63 | 64 | * Make the instructions on how to restore a key without paperkey a 65 | bit clearer. 66 | 67 | 68 | Noteworthy changes in version 1.0 (2009-01-22) 69 | ---------------------------------------------- 70 | 71 | * Minor documentation and build changes. 72 | 73 | * Gnulib refresh. 74 | 75 | 76 | Noteworthy changes in version 0.8 (2008-02-01) 77 | ---------------------------------------------- 78 | 79 | * The file format is now included as part of the base16 output, as 80 | there is no guarantee that this program will be on-hand when a 81 | reconstruction is necessary. The format can also be displayed 82 | via the --file-format command. Suggested by Brendan Kidwell. 83 | 84 | * Some bug fixes (actually to gnulib, but relevant here as well) 85 | to the SHA-1 code on platforms that require aligned access. 86 | Thanks to Peter Palfrader. 87 | 88 | * New --comment option to add comments to the base16 output. 89 | 90 | 91 | Noteworthy changes in version 0.7 (2007-10-03) 92 | ---------------------------------------------- 93 | 94 | * Man page contributed by Peter Palfrader. 95 | 96 | * Secret data can now be passed in on standard input if desired. 97 | 98 | 99 | Noteworthy changes in version 0.6 (2007-09-22) 100 | ---------------------------------------------- 101 | 102 | * Bug fix for certain key sizes thanks to Peter Palfrader. 103 | 104 | * Add versioning to the output format. 105 | 106 | * Add an overall CRC to the secrets output to catch certain types 107 | of corruption. 108 | 109 | * Properly use a single public key out of a whole public keyring 110 | when restoring. 111 | 112 | 113 | Noteworthy changes in version 0.5 (2007-09-21) 114 | ---------------------------------------------- 115 | 116 | * Initial release. 117 | -------------------------------------------------------------------------------- /restore.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007, 2012 David Shaw 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include "packets.h" 26 | #include "output.h" 27 | #include "parse.h" 28 | #include "restore.h" 29 | 30 | struct key 31 | { 32 | unsigned char fpr[20]; 33 | struct packet *packet; 34 | struct key *next; 35 | }; 36 | 37 | static struct key * 38 | extract_keys(struct packet *packet) 39 | { 40 | struct key *key=NULL; 41 | size_t idx=1; 42 | 43 | /* Check the version */ 44 | if(packet->len && packet->buf[0]!=0) 45 | { 46 | fprintf(stderr,"Cannot handle secrets file version %d\n", 47 | packet->buf[0]); 48 | return NULL; 49 | } 50 | 51 | while(idxlen) 52 | { 53 | /* 1+20+2 == version + fingerprint + length */ 54 | if(idx+1+20+2<=packet->len) 55 | { 56 | if(packet->buf[idx]==4) 57 | { 58 | unsigned int len; 59 | struct key *newkey; 60 | 61 | newkey=xmalloc(sizeof(*newkey)); 62 | newkey->next=NULL; 63 | 64 | idx++; 65 | memcpy(newkey->fpr,&packet->buf[idx],20); 66 | 67 | idx+=20; 68 | 69 | len =packet->buf[idx++]<<8; 70 | len|=packet->buf[idx++]; 71 | 72 | if(idx+len<=packet->len) 73 | { 74 | newkey->packet=append_packet(NULL,&packet->buf[idx],len); 75 | idx+=len; 76 | } 77 | else 78 | { 79 | fprintf(stderr,"Warning: Short data in secret image\n"); 80 | free(newkey); 81 | break; 82 | } 83 | 84 | newkey->next=key; 85 | key=newkey; 86 | } 87 | else 88 | { 89 | fprintf(stderr,"Warning: Corrupt data in secret image\n"); 90 | break; 91 | } 92 | } 93 | else 94 | { 95 | fprintf(stderr,"Warning: Short header in secret image\n"); 96 | break; 97 | } 98 | } 99 | 100 | return key; 101 | } 102 | 103 | static void 104 | free_keys(struct key *key) 105 | { 106 | while(key) 107 | { 108 | struct key *keytmp=key; 109 | free_packet(key->packet); 110 | key=key->next; 111 | free(keytmp); 112 | } 113 | } 114 | 115 | int 116 | restore(FILE *pubring,FILE *secrets, 117 | enum data_type input_type,const char *outname) 118 | { 119 | struct packet *secret; 120 | 121 | if(input_type==AUTO) 122 | { 123 | int test=fgetc(secrets); 124 | 125 | if(test==EOF) 126 | { 127 | fprintf(stderr,"Unable to check type of secrets file\n"); 128 | return 1; 129 | } 130 | else if(isascii(test) && isprint(test)) 131 | input_type=BASE16; 132 | else 133 | input_type=RAW; 134 | 135 | ungetc(test,secrets); 136 | } 137 | 138 | secret=read_secrets_file(secrets,input_type); 139 | if(secret) 140 | { 141 | struct packet *pubkey; 142 | struct key *keys; 143 | int did_pubkey=0; 144 | 145 | /* Build a list of all keys. We need to do this since the 146 | public key we are transforming might have the subkeys in a 147 | different order than (or not match subkeys at all with) our 148 | secret data. */ 149 | 150 | keys=extract_keys(secret); 151 | if(keys) 152 | { 153 | output_start(outname,RAW,NULL); 154 | 155 | while((pubkey=parse(pubring,0,0))) 156 | { 157 | unsigned char ptag; 158 | 159 | if(pubkey->type==6 || pubkey->type==14) 160 | { 161 | /* Public key or subkey */ 162 | unsigned char fpr[20]; 163 | struct key *keyidx; 164 | 165 | if(pubkey->type==6 && did_pubkey) 166 | break; 167 | 168 | calculate_fingerprint(pubkey,pubkey->len,fpr); 169 | 170 | /* Do we have a secret key that matches? */ 171 | for(keyidx=keys;keyidx;keyidx=keyidx->next) 172 | { 173 | if(memcmp(fpr,keyidx->fpr,20)==0) 174 | { 175 | if(pubkey->type==6) 176 | { 177 | ptag=5; 178 | did_pubkey=1; 179 | } 180 | else 181 | ptag=7; 182 | 183 | /* Match, so create a secret key. */ 184 | output_openpgp_header(ptag,pubkey->len 185 | +keyidx->packet->len); 186 | output_packet(pubkey); 187 | output_packet(keyidx->packet); 188 | } 189 | } 190 | } 191 | else if(did_pubkey) 192 | { 193 | /* Copy the usual user ID, sigs, etc, so the key is 194 | well-formed. */ 195 | output_openpgp_header(pubkey->type,pubkey->len); 196 | output_packet(pubkey); 197 | } 198 | 199 | free_packet(pubkey); 200 | } 201 | 202 | free_keys(keys); 203 | } 204 | else 205 | { 206 | fprintf(stderr,"Unable to parse secret data\n"); 207 | return 1; 208 | } 209 | } 210 | else 211 | { 212 | fprintf(stderr,"Unable to read secrets file\n"); 213 | return 1; 214 | } 215 | 216 | return 0; 217 | } 218 | -------------------------------------------------------------------------------- /paperkey.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007, 2008, 2009, 2012, 2016 David Shaw 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "output.h" 28 | #include "extract.h" 29 | #include "restore.h" 30 | 31 | int verbose=0,ignore_crc_error=0; 32 | unsigned int output_width=78; 33 | char *comment=NULL; 34 | 35 | enum options 36 | { 37 | OPT_HELP=256, 38 | OPT_VERSION, 39 | OPT_VERBOSE, 40 | OPT_OUTPUT, 41 | OPT_INPUT_TYPE, 42 | OPT_OUTPUT_TYPE, 43 | OPT_OUTPUT_WIDTH, 44 | OPT_SECRET_KEY, 45 | OPT_PUBRING, 46 | OPT_SECRETS, 47 | OPT_IGNORE_CRC_ERROR, 48 | OPT_FILE_FORMAT, 49 | OPT_COMMENT 50 | }; 51 | 52 | static struct option long_options[]= 53 | { 54 | {"help",no_argument,NULL,OPT_HELP}, 55 | {"version",no_argument,NULL,OPT_VERSION}, 56 | {"verbose",no_argument,NULL,OPT_VERBOSE}, 57 | {"output",required_argument,NULL,OPT_OUTPUT}, 58 | {"input-type",required_argument,NULL,OPT_INPUT_TYPE}, 59 | {"output-type",required_argument,NULL,OPT_OUTPUT_TYPE}, 60 | {"output-width",required_argument,NULL,OPT_OUTPUT_WIDTH}, 61 | {"secret-key",required_argument,NULL,OPT_SECRET_KEY}, 62 | {"pubring",required_argument,NULL,OPT_PUBRING}, 63 | {"secrets",required_argument,NULL,OPT_SECRETS}, 64 | {"ignore-crc-error",no_argument,NULL,OPT_IGNORE_CRC_ERROR}, 65 | {"file-format",no_argument,NULL,OPT_FILE_FORMAT}, 66 | {"comment",required_argument,NULL,OPT_COMMENT}, 67 | {NULL,0,NULL,0} 68 | }; 69 | 70 | static void 71 | usage(void) 72 | { 73 | printf("Usage: paperkey [OPTIONS]\n"); 74 | printf(" --help (-h)\n"); 75 | printf(" --version (-V)\n"); 76 | printf(" --verbose (-v) be more verbose\n"); 77 | printf(" --output (-o) write output to this file\n"); 78 | printf(" --input-type auto, base16 or raw (binary)\n"); 79 | printf(" --output-type base16 or raw (binary)\n"); 80 | printf(" --output-width maximum width of base16 output\n"); 81 | printf(" --secret-key" 82 | " extract secret data from this secret key\n"); 83 | printf(" --pubring" 84 | " public keyring to find non-secret data\n"); 85 | printf(" --secrets file containing secret" 86 | " data to join with the public key\n"); 87 | printf(" --ignore-crc-error don't reject corrupted input\n"); 88 | printf(" --file-format show the paperkey file format\n"); 89 | printf(" --comment add a comment to the base16 output\n"); 90 | } 91 | 92 | int 93 | main(int argc,char *argv[]) 94 | { 95 | int arg,err; 96 | FILE *secret_key,*secrets,*pubring=NULL; 97 | const char *outname=NULL; 98 | enum data_type output_type=BASE16; 99 | enum data_type input_type=AUTO; 100 | 101 | set_binary_mode(stdin); 102 | 103 | secret_key=secrets=stdin; 104 | 105 | /* Force the umask to go-rwx, as we are going to be creating files 106 | that contain secret key material. */ 107 | umask(077); 108 | 109 | while((arg=getopt_long(argc,argv,"hVvo:",long_options,NULL))!=-1) 110 | switch(arg) 111 | { 112 | case OPT_HELP: 113 | case 'h': 114 | default: 115 | usage(); 116 | exit(0); 117 | 118 | case OPT_VERSION: 119 | case 'V': 120 | printf("%s (%s)\n",PACKAGE_STRING,HOST_OS); 121 | printf("%s\n",COPYRIGHT_STRING); 122 | printf("This is free software. You may redistribute copies of it" 123 | " under the terms of\n"); 124 | printf("the GNU General Public License" 125 | " .\n"); 126 | printf("There is NO WARRANTY, to the extent permitted by law.\n\n"); 127 | exit(0); 128 | 129 | case OPT_VERBOSE: 130 | case 'v': 131 | verbose++; 132 | break; 133 | 134 | case OPT_OUTPUT: 135 | case 'o': 136 | outname=optarg; 137 | break; 138 | 139 | case OPT_INPUT_TYPE: 140 | if(strcmp(optarg,"auto")==0) 141 | input_type=AUTO; 142 | else if(strcmp(optarg,"base16")==0) 143 | input_type=BASE16; 144 | else if(strcmp(optarg,"raw")==0) 145 | input_type=RAW; 146 | else 147 | { 148 | fprintf(stderr,"Unknown input type \"%s\"\n",optarg); 149 | exit(1); 150 | } 151 | break; 152 | 153 | case OPT_OUTPUT_TYPE: 154 | if(strcmp(optarg,"base16")==0) 155 | output_type=BASE16; 156 | else if(strcmp(optarg,"raw")==0) 157 | output_type=RAW; 158 | else 159 | { 160 | fprintf(stderr,"Unknown output type \"%s\"\n",optarg); 161 | exit(1); 162 | } 163 | break; 164 | 165 | case OPT_OUTPUT_WIDTH: 166 | output_width=atoi(optarg); 167 | break; 168 | 169 | case OPT_SECRET_KEY: 170 | secret_key=fopen(optarg,"rb"); 171 | if(!secret_key) 172 | { 173 | fprintf(stderr,"Unable to open %s: %s\n",optarg,strerror(errno)); 174 | exit(1); 175 | } 176 | break; 177 | 178 | case OPT_PUBRING: 179 | pubring=fopen(optarg,"rb"); 180 | if(!pubring) 181 | { 182 | fprintf(stderr,"Unable to open pubring %s: %s\n", 183 | optarg,strerror(errno)); 184 | exit(1); 185 | } 186 | break; 187 | 188 | case OPT_SECRETS: 189 | secrets=fopen(optarg,"rb"); 190 | if(!secrets) 191 | { 192 | fprintf(stderr,"Unable to open %s: %s\n",optarg,strerror(errno)); 193 | exit(1); 194 | } 195 | break; 196 | 197 | case OPT_IGNORE_CRC_ERROR: 198 | ignore_crc_error=1; 199 | break; 200 | 201 | case OPT_FILE_FORMAT: 202 | output_file_format(stdout,""); 203 | exit(0); 204 | 205 | case OPT_COMMENT: 206 | comment=optarg; 207 | break; 208 | } 209 | 210 | if(pubring) 211 | err=restore(pubring,secrets,input_type,outname); 212 | else 213 | err=extract(secret_key,outname,output_type); 214 | 215 | return err; 216 | } 217 | -------------------------------------------------------------------------------- /paperkey.1: -------------------------------------------------------------------------------- 1 | .\" paperkey manpage copyright (C) 2007, 2012, 2013 Peter Palfrader 2 | .\" Examples have been taken from David Shaw's README. 3 | .\" 4 | .\" This document is free software; you can redistribute it and/or modify 5 | .\" it under the terms of the GNU General Public License as published by 6 | .\" the Free Software Foundation; either version 2 of the License, or 7 | .\" (at your option) any later version. 8 | .\" 9 | .\" This document is distributed in the hope that it will be useful, 10 | .\" but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | .\" GNU General Public License for more details. 13 | .\" 14 | .\" You should have received a copy of the GNU General Public License 15 | .\" along with this program; if not, write to the Free Software 16 | .\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 17 | .\" MA 02110-1301 USA 18 | .\" 19 | .\" 20 | .\" paperkey, the software, is written and 21 | .\" copyright 2007, 2008 David Shaw 22 | .TH PAPERKEY 1 "June 2012" "PAPERKEY" 23 | .SH NAME 24 | paperkey \- extract secret information out of OpenPGP secret keys 25 | .SH SYNOPSIS 26 | .B paperkey\fR 27 | [\fB\-\-secret\-key=\fR\fIFILE\fR] 28 | [\fB\-\-output=\fR\fIFILE\fR] 29 | [\fB\-\-output\-type=\fR\fBbase16\fR|\fBraw\fR] 30 | [\fB\-\-output\-width=\fR\fIWIDTH\fR] 31 | .LP 32 | .B paperkey\fR 33 | \fB\-\-pubring=\fR\fIFILE\fR 34 | [\fB\-\-secrets=\fR\fIFILE\fR] 35 | [\fB\-\-input\-type=\fR\fBauto\fR|\fBbase16\fR|\fBraw\fR] 36 | [\fB\-\-output=\fR\fIFILE\fR] 37 | [\fB\-\-ignore\-crc\-error\fR] 38 | [\fB\-\-comment=\fR\fISTRING\fR] 39 | [\fB\-\-file\-format\fR] 40 | .LP 41 | .B paperkey\fR \fB\-\-version\fR 42 | .SH MOTIVATION 43 | As with all data, secret keys should be backed up. In fact, secret 44 | keys should be backed up even better than other data, because they are 45 | impossible to recreate should they ever be lost. All files encrypted 46 | to lost keys are forever (or at least for a long time) undecipherable. 47 | In addition to keeping backups of secret key information on digital 48 | media such as USB-sticks or CDs it is reasonable to keep an 49 | if-all-else-fails copy on plain old paper, for use should your digital 50 | media ever become unreadable for whatever reason. Stored properly, 51 | paper is able to keep information for several decades or longer. 52 | .PP 53 | With GnuPG, PGP, or other OpenPGP implementations the secret key 54 | usually contains a lot more than just the secret numbers that are 55 | important. They also hold all the public values of key pairs, user 56 | ids, expiration times and more. In order to minimize the information 57 | that has to be entered manually or with the help of OCR, QR code or 58 | similar software, \fBpaperkey\fR extracts just the secret information 59 | out of OpenPGP secret keys. For recovering a secret key it is assumed 60 | that the public key is still available, for instance from public 61 | Internet keyservers. 62 | .SH DESCRIPTION 63 | \fBpaperkey\fR has two modes of operation: 64 | .PP 65 | The first mode creates "paperkeys" by extracting just the secret 66 | information from a secret key, formatting the data in a way suitable 67 | for printing or in a raw mode for further processing. 68 | .PP 69 | The other mode rebuilds secret keys from such a paperkey and a copy of 70 | the public key, also verifying the checksums embedded in the paperkey. 71 | This mode is selected when the \fB\-\-pubring\fR option is used, which 72 | is required in that case. If a passphrase was set on the original 73 | secret key, the same passphrase is set on the rebuilt key. 74 | .PP 75 | Input is read from standard\-in except when the \fB\-\-secret\-key\fR or 76 | \fB\-\-secrets\fR option is used; output is printed to standard\-out, 77 | unless changed with the \fB\-\-output\fR option. 78 | .SH SECURITY CONSIDERATIONS 79 | Please note that \fBpaperkey\fR does not change the protection and 80 | encryption status of and security requirements for storing your secret 81 | key. If the secret key was protected by a passphrase so is the 82 | paperkey. If the secret key was unprotected the paperkey will not be 83 | protected either. 84 | .SH OPTIONS 85 | \fB\-\-help\fR, \fB\-h\fR 86 | Display a short help message and exit successfully. 87 | .LP 88 | .TP 89 | \fB\-\-version\fR, \fB\-V\fR 90 | Print version information and copyright information and exit successfully. 91 | .LP 92 | .TP 93 | \fB\-\-verbose\fR, \fB\-v\fR 94 | Print status and progress information to standard\-error while processing 95 | the input. Repeat for even more output. 96 | .LP 97 | .TP 98 | \fB\-\-output=\fR\fIFILE\fR, \fB\-o\fR 99 | Redirect output to the file given instead of printing to standard\-output. 100 | .LP 101 | .TP 102 | \fB\-\-comment=\fR\fISTRING\fR 103 | Include the specified comment in the base16 output. 104 | .LP 105 | .TP 106 | \fB\-\-file\-format\fR 107 | Paperkey automatically includes the file format it uses as comments at 108 | the top of the base16 output. This command simply prints out the file 109 | format and exits successfully. 110 | .SH OPTIONS FOR EXTRACTING SECRET INFORMATION 111 | .TP 112 | \fB\-\-output\-type=base16\fR, \fB\-\-output\-type=raw\fR 113 | Select the output type. The base16 style encodes the information in 114 | the style of a classic hex-dump, including line numbers and per-line 115 | CRC checksums to facilitate localizing errors in the input file during 116 | the recovery phase. The raw, or binary, mode is just a raw dump of 117 | the secret information, intended for feeding to barcode generators or 118 | the like. 119 | .LP 120 | .TP 121 | \fB\-\-output\-width=\fR\fIWIDTH\fR 122 | Choose line width in the base16 output mode. The default is 78 characters. 123 | .LP 124 | .TP 125 | \fB\-\-secret\-key=\fR\fIFILE\fR 126 | File to read the secret key from. If this option is not given \fBpaperkey\fR 127 | reads from standard\-input. 128 | .SH OPTIONS FOR RE-CREATING PRIVATE KEYS 129 | .TP 130 | \fB\-\-input\-type=auto\fR, \fB\-\-input\-type=base16\fR, \fB\-\-input\-type=raw\fR 131 | Specify that the given input is either in base16 format, as produced 132 | by \fBpaperkey\fR, or in raw format. The default, auto, tries to 133 | automatically detect the format in use. 134 | .LP 135 | .TP 136 | \fB\-\-pubring=\fR\fIFILE\fR 137 | File to read public key information from. It is assumed that the user can 138 | get the public key from sources like public Internet keyservers. 139 | .LP 140 | .TP 141 | \fB\-\-secrets=\fR\fIFILE\fR 142 | File to read the extracted secrets, the paperkey, from. If this is not given 143 | then the information is read from standard\-input. 144 | .LP 145 | .TP 146 | \fB\-\-ignore\-crc\-error\fR 147 | Do not reject corrupt input and continue despite any CRC errors. 148 | .SH EXAMPLES 149 | Take the secret key in key.gpg and generate a text file 150 | to\-be\-printed.txt that contains the secret data: 151 | .PP 152 | .B $ paperkey \-\-secret\-key my\-secret\-key.gpg \-\-output to\-be\-printed.txt 153 | .PP 154 | Take the secret key data in my\-key\-text\-file.txt and combine it with 155 | my\-public\-key.gpg to reconstruct my\-secret\-key.gpg: 156 | .PP 157 | .B $ paperkey \-\-pubring my\-public\-key.gpg \-\-secrets my\-key\-text\-file.txt \-\-output my\-secret\-key.gpg 158 | .PP 159 | If \-\-output is not specified, the output goes to stdout. If \-\-secret\-key is 160 | not specified, the data is read from stdin so you can do things like: 161 | .PP 162 | .B $ gpg \-\-export\-secret\-key my\-key | paperkey | lpr 163 | .SH SEE ALSO 164 | .BR gpg (1), 165 | .BR http://www.jabberwocky.com/software/paperkey/ 166 | .SH AUTHORS 167 | \fBpaperkey\fR is written by David Shaw . 168 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | 2017-11-17 David Shaw 2 | 3 | * checks: Various improvements to allow running the selftests in 4 | parallel (Debian bug 881792). Reported by Daniel Kahn Gillmor. 5 | 6 | 2017-10-23 David Shaw 7 | 8 | * extract.c (extract): Properly fail if we cannot parse a secret 9 | subkey packet. Reported by Osamu Aoki and Peter Palfrader. 10 | 11 | * parse.c (extract_secrets): Add support for EdDSA as per 12 | https://tools.ietf.org/html/draft-koch-eddsa-for-openpgp-04. Note 13 | that this is an expired draft, but GnuPG is using algorithm 22 for 14 | it. 15 | 16 | 2016-09-05 David Shaw 17 | 18 | * NEWS, configure.ac: Post-release commit. 19 | 20 | * NEWS, configure.ac: Preparing 1.4 release. 21 | 22 | * NEWS, output.h, output.c, paperkey.c (set_binary_mode): Fix 23 | binary vs text output mismatch when using "--output-type raw" on 24 | Windows platforms. Does not affect any non-Windows platforms. 25 | Found by Thierry Ambrus. 26 | 27 | * README: Clarify output-width a bit. 28 | 29 | * paperkey.spec.in: Use %license for the COPYING file and run the 30 | self checks during build. 31 | 32 | 2016-09-04 David Shaw 33 | 34 | * README: PowerPC Macs are basically gone, so remove the OSX 35 | (sorry: macOS!) universal binary stuff. 36 | 37 | 2013-01-03 David Shaw 38 | 39 | * NEWS, configure.ac: Post-release commit. 40 | 41 | * NEWS, configure.ac: Preparing 1.3 release. 42 | 43 | * README: Clarify language a bit. 44 | 45 | 2012-11-29 David Shaw 46 | 47 | * checks/roundtrip.sh, checks/roundtrip-raw.sh: Use /bin/echo as 48 | some shell builtin echoes don't understand "echo -n". 49 | 50 | * checks: Add tests for RSA and ECC keys. 51 | 52 | * output.c (output_openpgp_header): Use the same "tag under 16, 53 | use old-style packets" as other OpenPGP programs. It's crazy to 54 | have a roundtrip test that doesn't compare the result with the 55 | input. 56 | 57 | 2012-06-13 David Shaw 58 | 59 | * parse.c (extract_secrets): Add support for ECDH and ECDSA as per 60 | RFC 6637. 61 | 62 | * paperkey.1: Internet is capitalized. 63 | 64 | * restore.c (restore): Fix problem with missing sigs on 65 | regenerated key. 66 | 67 | 2009-10-15 David Shaw 68 | 69 | * NEWS, configure.ac: Post-release commit. 70 | 71 | 2009-10-14 David Shaw 72 | 73 | * NEWS, configure.ac: Preparing 1.2 release. 74 | 75 | * paperkey.c (main): Force the umask to go-rwx, as we are going to 76 | be creating files that contain secret key material. 77 | 78 | * paperkey.1, paperkey.c (main, usage): Add -o as alias to 79 | --output, and document -h (help), -V (version), and -v (verbose). 80 | 81 | * README: Clarify how to build a 32-bit/64-bit OSX universal 82 | binary. 83 | 84 | 2009-02-10 David Shaw 85 | 86 | * NEWS, configure.ac: Post-release commit. 87 | 88 | * NEWS, configure.ac: Preparing 1.1 release. 89 | 90 | * paperkey.1: Don't mention the README file. It's not really 91 | necessary here, and not all platfoms install it. Noted by Todd 92 | Zullinger. 93 | 94 | * output.c (output_file_format, output_start): Some file format 95 | documentation tweaks. 96 | 97 | 2009-01-27 David Shaw 98 | 99 | * paperkey.c (main): Properly set up stdin to be in binary mode on 100 | Win32. 101 | 102 | 2009-01-22 David Shaw 103 | 104 | * NEWS, configure.ac: Post-release commit. 105 | 106 | * README: Update. 107 | 108 | * configure.ac: Preparing 1.0 release. 109 | 110 | 2009-01-16 David Shaw 111 | 112 | * paperkey.c (main): --version strings from autoconf. 113 | 114 | * configure.ac: autoupdate, and some m4 magic so we can propagate 115 | the copyright line into config.h. 116 | 117 | * paperkey.spec.in: Borrow some RPM-fu from the Fedora RPM by Todd 118 | Zullinger. 119 | 120 | * output.c (output_file_format): Clarify we're all big endian 121 | here. Noted by Frédéric Brière. 122 | 123 | 2009-01-14 David Shaw 124 | 125 | * Makefile.am: Autogenerate paperkey.txt from paperkey.1 when 126 | cross compiling. 127 | 128 | * configure.ac, Makefile.am: Don't run selftest when cross 129 | compiling. 130 | 131 | * Makefile.am: When cross compiling for Win32, add README.WIN32, a 132 | regular text file version of the man page, and the Win32 binary. 133 | 134 | * README: Clarify what universal binaries are good for. 135 | 136 | * configure.ac: Add "-win32" to dist version string when cross 137 | compiling. 138 | 139 | * README.WIN32: New. 140 | 141 | 2008-02-01 David Shaw 142 | 143 | * NEWS, configure.ac: Post-release commit. 144 | 145 | * NEWS, configure.ac: Preparing 0.8 release. 146 | 147 | 2008-01-31 David Shaw 148 | 149 | * parse.c (read_secrets_file): Skip \r lines as well for Win32. 150 | 151 | * NEWS: Update for 0.8: --file-format, --comment, and SHA-1 fixes. 152 | 153 | * paperkey.c (usage, main), output.c (output_start): Add --comment 154 | option to include an arbitrary comment in base16 output. 155 | 156 | * paperkey.1: Document --file-format and --comment. 157 | 158 | * README, paperkey.spec.in: Slightly more elegant language. 159 | 160 | 2008-01-30 David Shaw 161 | 162 | * parse.c (read_secrets_file): Ignore blank lines in text input. 163 | 164 | 2007-11-02 David Shaw 165 | 166 | * paperkey.1: Escape dashes in man page. From Peter Palfrader. 167 | 168 | 2007-11-01 David Shaw 169 | 170 | * output.h, output.c (output_file_format): New function to print 171 | our file format into the archive. 172 | 173 | * paperkey.c (main), output.c (output_start): Use it here. 174 | Suggested by Brendan Kidwell. 175 | 176 | 2007-10-03 David Shaw 177 | 178 | * configure.ac: NEWS: Post-release commit. 179 | 180 | * README, configure.ac, NEWS: Preparing 0.7 release. 181 | 182 | 2007-09-25 David Shaw 183 | 184 | * paperkey.c (main): There is no such thing as an "auto" 185 | --output-type. 186 | 187 | * parse.c (parse): Just in case, check for a short read on the 188 | packet. 189 | 190 | 2007-09-24 David Shaw 191 | 192 | * AUTHORS, Makefile.am, paperkey.spec.in, paperkey.1: Add man page 193 | thanks to Peter Palfrader. 194 | 195 | * restore.h, restore.c (restore): Pass in secrets FILE* from 196 | outside. Don't do the reopen-as-text trickery since even Win32 197 | doesn't need it. 198 | paperkey.c (main): Call it from here. This allows for secrets to 199 | come in on stdin. 200 | 201 | 2007-09-22 David Shaw 202 | 203 | * configure.ac, NEWS: Release version 0.6. 204 | 205 | * output.h, output.c (do_crc24): Better CRC24 implementation. 206 | parse.c (read_secrets_file), output.c (output_bytes): Call it 207 | here. 208 | 209 | * README: Bar codes, of course, have many of the disadvantages of 210 | non-human-readable media. 211 | 212 | * paperkey.c (usage): Options. 213 | 214 | * parse.c (read_secrets_file), output.c (output_finish, 215 | output_bytes, print_base16): Add a CRC on RAW output as well. 216 | (parse): Note that we don't support armored data. 217 | 218 | * NEWS, README: Update. 219 | 220 | * parse.c (read_secrets_file), output.c (print_base16): Add an 221 | overall CRC that covers the whole secrets file. 222 | 223 | * output.c (output_length16): Fix sizing bug. Thanks to Peter 224 | Palfrader. 225 | (output_start): Show what version of paperkey generated a given 226 | file. 227 | 228 | * extract.c (extract), restore.c (extract_keys): Include a version 229 | number in the secrets, in case the format needs to change in the 230 | future. 231 | 232 | * restore.c (extract_keys): Some more rigorous consistency 233 | checking when pulling apart the secret image. It's possible that 234 | OCR or the like mangled it. 235 | 236 | * parse.c (extract_secrets): We're not going to support v3 keys. 237 | 238 | * restore.c (restore): Properly handle extracting a public key out 239 | from a large keyring so we can just pass our pubring directly to 240 | --pubring. 241 | 242 | * packets.h, output.c, output.h, extract.c, parse.c, parse.h, 243 | extract.h, paperkey.c, restore.c, restore.h, packets.c: Add 244 | copyright statements (oops). Thanks to Peter Palfrader. 245 | 246 | 2007-09-21 David Shaw 247 | 248 | * configure.ac: Release version 0.5. 249 | -------------------------------------------------------------------------------- /output.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007, 2008, 2009, 2012, 2016 David Shaw 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #ifdef _WIN32 26 | #include 27 | #include 28 | #endif 29 | #include "packets.h" 30 | #include "output.h" 31 | 32 | extern unsigned int output_width; 33 | extern char *comment; 34 | 35 | static enum data_type output_type; 36 | static FILE *output; 37 | static unsigned int line_items; 38 | static unsigned long all_crc=CRC24_INIT; 39 | 40 | #define CRC24_POLY 0x864CFBL 41 | 42 | void 43 | do_crc24(unsigned long *crc,const unsigned char *buf,size_t len) 44 | { 45 | size_t i; 46 | 47 | for(i=0;i>16; 196 | crc[1]=(all_crc&0xFFFFFFL)>>8; 197 | crc[2]=(all_crc&0xFFFFFFL); 198 | 199 | ret=fwrite(crc,1,3,output); 200 | } 201 | else 202 | ret=fwrite(buf,1,length,output); 203 | break; 204 | 205 | case AUTO: 206 | case BASE16: 207 | print_base16(buf,length); 208 | ret=length; 209 | break; 210 | } 211 | 212 | return ret; 213 | } 214 | 215 | ssize_t 216 | output_length16(size_t length) 217 | { 218 | unsigned char encoded[2]; 219 | 220 | assert(length<=65535); 221 | 222 | encoded[0]=length>>8; 223 | encoded[1]=length; 224 | 225 | return output_bytes(encoded,2); 226 | } 227 | 228 | ssize_t 229 | output_openpgp_header(unsigned char tag,size_t length) 230 | { 231 | unsigned char encoded[6]; 232 | size_t bytes; 233 | 234 | /* We use the same "tag under 16, use old-style packets" rule that 235 | many OpenPGP programs do. This helps make the resulting key 236 | byte-for-byte identical. It's not a guarantee, as it is legal 237 | for the generating program to use whatever packet style it likes, 238 | but does help avoid questions why the input to paperkey might not 239 | equal the output. */ 240 | 241 | if(tag<16) 242 | { 243 | if(length>65535) 244 | { 245 | encoded[0]=0x80|(tag<<2)|2; 246 | encoded[1]=length>>24; 247 | encoded[2]=length>>16; 248 | encoded[3]=length>>8; 249 | encoded[4]=length; 250 | bytes=5; 251 | } 252 | else if(length>255) 253 | { 254 | encoded[0]=0x80|(tag<<2)|1; 255 | encoded[1]=length>>8; 256 | encoded[2]=length; 257 | bytes=3; 258 | } 259 | else 260 | { 261 | encoded[0]=0x80|(tag<<2); 262 | encoded[1]=length; 263 | bytes=2; 264 | } 265 | } 266 | else 267 | { 268 | encoded[0]=0xC0|tag; 269 | 270 | if(length>8383) 271 | { 272 | encoded[1]=0xFF; 273 | encoded[2]=length>>24; 274 | encoded[3]=length>>16; 275 | encoded[4]=length>>8; 276 | encoded[5]=length; 277 | bytes=6; 278 | } 279 | else if(length>191) 280 | { 281 | encoded[1]=192+((length-192)>>8); 282 | encoded[2]=(length-192); 283 | bytes=3; 284 | } 285 | else 286 | { 287 | encoded[1]=length; 288 | bytes=2; 289 | } 290 | } 291 | 292 | return output_bytes(encoded,bytes); 293 | } 294 | 295 | void 296 | output_finish(void) 297 | { 298 | output_bytes(NULL,0); 299 | } 300 | 301 | void 302 | set_binary_mode(FILE *stream) 303 | { 304 | #ifdef _WIN32 305 | if(_setmode(_fileno(stream),_O_BINARY)==-1) 306 | { 307 | fprintf(stderr,"Unable to set stream mode to binary: %s\n", 308 | strerror(errno)); 309 | exit(1); 310 | } 311 | #else 312 | (void)stream; 313 | #endif 314 | } 315 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Paperkey - an OpenPGP key archiver 2 | 3 | ### David Shaw 4 | 5 | 6 | A reasonable way to achieve a long term backup of OpenPGP (GnuPG, PGP, 7 | etc) keys is to print them out on paper. Paper and ink have amazingly 8 | long retention qualities - far longer than the magnetic or optical 9 | means that are generally used to back up computer data. 10 | 11 | 12 | Paper? Seriously? 13 | ------------------ 14 | 15 | The goal with paper is not secure storage. There are countless ways 16 | to store something securely. A paper backup also isn't a replacement 17 | for the usual machine readable (tape, CD-R, DVD-R, etc) backups, but 18 | rather as an if-all-else-fails method of restoring a key. Most of the 19 | storage media in use today do not have particularly good long-term 20 | (measured in years to decades) retention of data. If and when the 21 | CD-R and/or tape cassette and/or USB key and/or hard drive the secret 22 | key is stored on becomes unusable, the paper copy can be used to 23 | restore the secret key. 24 | 25 | 26 | What paperkey does 27 | ------------------ 28 | 29 | Due to metadata and redundancy, OpenPGP secret keys are significantly 30 | larger than just the "secret bits". In fact, the secret key contains 31 | a complete copy of the public key. Since the public key generally 32 | doesn't need to be escrowed (most people have many copies of it on 33 | various keyservers, web pages, or similar), only archiving the secret 34 | parts can be a real advantage. 35 | 36 | Paperkey extracts just those secret bytes and prints them in a easily 37 | handled format. To reconstruct, you re-enter those bytes (whether by 38 | hand or via OCR) and paperkey uses them to transform your existing 39 | public key into a secret key. 40 | 41 | For example, the regular DSA+Elgamal secret key I just tested comes 42 | out to 1281 bytes. The secret parts of that key (plus some minor 43 | packet structure) come to only 149 bytes. It's a lot easier to 44 | re-enter 149 bytes correctly. 45 | 46 | Different key algorithms will benefit to a different degree from this 47 | size reduction. In general, DSA or Elgamal keys benefit the most, 48 | shrinking to around 10% of the original key size, and RSA keys benefit 49 | the least, only shrinking to about 50% of the original key size. ECC 50 | keys are in between, shrinking to around 20-25% of the original, but 51 | of course, ECC keys are quite small to begin with, and 25% of a small 52 | number can compare well to 10% of a larger number. 53 | 54 | As with any backup or archiving system, it is prudent to verify you 55 | can restore the key from your paper copy before filing the paper away. 56 | 57 | 58 | Aren't CD-Rs supposed to last a long time? 59 | ------------------------------------------ 60 | 61 | They're certainly advertised to (and I've seen some pretty incredible 62 | claims of 100 years or more), but in practice it doesn't really work 63 | out that way. The manufacturing of the media, the burn quality, the 64 | burner quality, the storage, etc, all have a significant impact on how 65 | long an optical disc will last. Some tests show that you're lucky to 66 | get 10 years. 67 | 68 | In comparison, to claim that paper will last for 100 years is not even 69 | vaguely impressive. High-quality paper with good ink regularly lasts 70 | many hundreds of years even under less than optimal conditions. 71 | 72 | Another bonus is that ink on paper is readable by humans. Not all 73 | backup methods will be readable 50 years later, so even if you have 74 | the backup, you can't easily buy a drive to read it. I doubt this 75 | will happen anytime soon with CD-R as there are just so many of them 76 | out there, but the storage industry is littered with old, now-dead 77 | methods of storing data. 78 | 79 | 80 | Security 81 | -------- 82 | 83 | Note that paperkey does not change the security requirements of 84 | storing a secret key. In fact, paperkey doesn't do any crypto at all, 85 | but just saves and restores the original secret key, whether it is 86 | encrypted or not. If your key has a passphrase on it (i.e. is 87 | encrypted), the paper copy is similarly encrypted. If your key has no 88 | passphrase, neither does the paper copy. Whatever the passphrase (or 89 | lack thereof) was on the original secret key will be the same on the 90 | reconstructed key. 91 | 92 | 93 | Examples 94 | -------- 95 | 96 | Take the secret key in my-secret-key.gpg and generate a text file 97 | to-be-printed.txt that contains the secret data: 98 | 99 | paperkey --secret-key my-secret-key.gpg --output to-be-printed.txt 100 | 101 | Take the secret key data in my-key-text-file.txt and combine it with 102 | my-public-key.gpg to reconstruct my-secret-key.gpg: 103 | 104 | paperkey --pubring my-public-key.gpg --secrets my-key-text-file.txt --output my-secret-key.gpg 105 | 106 | If --output is not specified, the output goes to stdout. If 107 | --secret-key is not specified, the data is read from stdin so you can 108 | do things like: 109 | 110 | gpg --export-secret-key my-key | paperkey | lpr 111 | 112 | Some other useful options are: 113 | 114 | --output-type can be "base16" or "raw". "base16" is human 115 | readable, and "raw" is useful if you want to pass 116 | the output to another program like a bar code or 117 | QR code generator (although note that scannable 118 | codes have some of the longevity disadvantages 119 | discussed above). Defaults to "base16". 120 | 121 | --input-type same as --output-type, but for the restore side of 122 | things. By default the input type is inferred 123 | automatically from the input data. 124 | 125 | --output-width sets the width of base16 output (i.e. given your font, 126 | how many columns fit on the paper you're printing on). 127 | Defaults to 78. 128 | 129 | --ignore-crc-error allows paperkey to continue when reconstructing 130 | even if it detects data corruption in the input. 131 | 132 | --verbose (or -v) be chatty about what is happening. Repeat this 133 | multiple times for more verbosity. 134 | 135 | Full documentation for all options is in the man page. 136 | 137 | 138 | RPM 139 | --- 140 | 141 | Paperkey ships with a RPM spec file. You can build an RPM with the 142 | usual 143 | ``` 144 | rpmbuild -ta /path/to/the/paperkey/tarball.tar.gz 145 | ``` 146 | 147 | Building 148 | -------- 149 | 150 | Paperkey uses [gnulib](https://www.gnu.org/software/gnulib/) to provide SHA-1 and getopt_long functionality as they're not available on all platforms. If you want to build directly from the github version, try this: 151 | 152 | ``` 153 | git clone git://git.savannah.gnu.org/gnulib.git # there is also a github mirror at https://github.com/coreutils/gnulib.git 154 | git clone https://github.com/dmshaw/paperkey.git 155 | cd paperkey 156 | ../gnulib/gnulib-tool --update 157 | autoreconf -f -i 158 | ./configure 159 | make check 160 | ``` 161 | 162 | Alternately, you can use the [releases in github](https://github.com/dmshaw/paperkey/releases). Those include the gnulib code as part of the package. 163 | 164 | 165 | Copyright 166 | --------- 167 | 168 | Paperkey is Copyright (C) 2007-2020 by David Shaw 169 | 170 | ``` 171 | -----BEGIN PGP PUBLIC KEY BLOCK----- 172 | 173 | mQILBDxUyXkBEACgg6vxNPigg9FQz14CkPtR/dEq3sCjK1r4+2oyeoRno+pqZ6Z7 174 | ZfphgA/q5woweFAGOg17KD2WXegoQ5pXbFvP+w9j9zm3g59XzTRSzZgScelTibPn 175 | Ky6g8r8GDAY6IQraR6pxe4297/NznqvRvKpTt5g1XP5LyjVBsEv9HAYJE1vyy10q 176 | SQRtEz3QunUzfELNC4kiYNMZOnmgaFeW4APIIhWDtrrxqW3Ofjp1K4DAhqcnayrf 177 | vYbOtqh0sxJ246kvVc3Bc9pH6wDw/yub2deuPq6BZBLBJwrtu/20qD0nsZ9is/5j 178 | 0aL1MZuVmr7xKYqeehyzJ1WdpJK52qng9natYedS+GefKDIw1Jq7ppQNWfVduTNI 179 | TFTF0JswggjQuPqKT8Td5GCywQWN/kGHbp6EdybiUXZ+9fp4eek0UB5M+srSwbkF 180 | 4hQ0mBrqlsaoji4CuXjc0c+Zx1D0pGfqqBCmvEV1tLul3U8h0TzR4opUA8mLKegQ 181 | p5cjh/dHz7zTPDxVgSr3blJ9FxI1Z69th/+jJj3q6joo3uW/5y8qQCrzdSCzs+TD 182 | EWwucZtJIuIhTct8AMPY/Ayt+Pf9jXfI+xSQgz3r7Eu5o+rEu02/cthaOc4b3KYD 183 | tNkjLKszgiext1BYOq06R+Yyh2qgsg9azzkfudvvpwhCpJ7EOxcdaP3bxwAGKbQl 184 | RGF2aWQgTS4gU2hhdyA8ZHNoYXdAamFiYmVyd29ja3kuY29tPokCOQQTAQIAIwIb 185 | AwIeAQIXgAUCTwxJwAYLCQgHAwIGFQoJCAsCBRYCAwEAAAoJENtpjXGZJCVgYt0P 186 | /23rpCNUquNkW1D2BcJOafaPrY9ieJEvGeWUSTQTctKq5F0wigoXwOZAmBg2/I7F 187 | Paorzf46ZmtkXq2e+wM/DQWqdVkS0eBIY10esf9hf6+CN/vvbTZBvPUhkeyCZVTE 188 | LCBMLTdMLGNAtjeuOovtS3OH+qlS7tefP7+plMxT0eBD+oywfsg6ijG2San4YnFt 189 | o18on5a5OjhhguVmyn1SqjhtRcmAftjZZrRvLEnQ/YRPbgA0vFRnafYaGhL9S8S/ 190 | JoroMfctnA2878GMhRljm+pCE0Ws+sK6FmlKvcTGWGOTxuPL3nV0ceXXEgjkXE3j 191 | O62iZsFX7y9avmKg4CRokIClbBKGjDkDiyF7l8r5m1A7WOqXqcSA3Jy1WQrV0x2N 192 | y1XRsJ00DMSl5oCcoESdTxXoCSS4LUhwSvYWmj82neol3PfGS9sGd/bYZddC8wMs 193 | 21R70CX57t5QzjmsEgWTiuA2XhXlYlKRe3KQA8N5ZZPJWLVzLDRatdCgyMxUruHi 194 | gTtH5Go5bLO2ayTdWJcVnB2yK6SDVt0zRJ8tHeHBcqbIUI9nJevnPcn1QQjTOTOZ 195 | pu2DLu3/b6CfrQKvy536qkBGSb+PWyLvtGlUbRhJXn3du2+7URP+sHhacaMgZNjk 196 | 5FPka4wa/63aeztJ4VmyQAP/5fDz+vvmmDZ0vf733YxPuQENBE8MSd0BCADFThAu 197 | kT/9zDYa4tv7+T8BvEmizNmVMaZ1/k7UTKHKnHT6Ns5TmIEQkmJ/wmEtNQSsLfOq 198 | ObJg00PUfDZtqb0oHEHsmUlUrJxBFifOKE0DApzZZTKRsrcU9V3KT1WVI9LbQc6X 199 | P90L81oTM//2yhuRd5VGLQ4KaDEA7eCDzJ5TbDScqyDsbmAe2hrmSg3y94X+g0eg 200 | paxaxyQzjlC1zk7ogG/U5rwnNqCK6cTDfIONR7Y0h7LVrpjI+/tDbhWkLYUAaY/W 201 | VgOl+nYAfOHmvVne3p5Sr05KrSnQenXuj+UFMtR1rf+5kwzmX701bGqIm6kB44DB 202 | tjif0f8HQoQzC9BlABEBAAGJA0QEGAECAA8CGwIFAlif4HoFCRLrIvMBKcBdIAQZ 203 | AQIABgUCTwxJ3QAKCRD+p4p6obxPpHxhB/9sNDyAsCSX6s1QPAecgJDGxhdIrE3Z 204 | 5Bx+O+DVG1cGlBIh3UpojPmxxyqF8koTqsAInLK2D8CeEqg8IB/IjI/LhiQf/Mby 205 | 57gB46jeThfecdcyhh97kHPhXj9Gea2u82YXIYYlVjD9VbsKggoSLFmda6YgPFd6 206 | KMbkwaIFgCNDK4GFVITq7HdHHDJCS5Iec/KnhnQNMHkFdQcycUwaiNSuvIK54LLe 207 | x2MAL/RoLpYNEmwxApyWlaghrJe6aRh44FHboBT9lAd7pJ1P2CXEdirrG+WM+DVE 208 | 7dluJF0bTQ94WtpRyMyCQSv07qBZVUtU/oUe2curJymo3Pv+zxcevmVkCRDbaY1x 209 | mSQlYHfbD/9ubFiCLQfBG2L2PcmZ3gqqeOwam5lTD9o5cXyWvObUdh1H+1OQ4eNG 210 | 2j/CAsGGSKDyOiQV++61j4YdT/58mIHqK1d6cUsdsErVvw27z/bmdcoZ1CxNp5yB 211 | 0Iz8WE/Jx9zFdb5/LEE7Z3J/SmpI14tNjnIwj+JvRBByH+MggB6AnDsBNkjsBHu9 212 | 7w85cwV9sb/vB5IHYBxsJYmm6vSqnOwxKoFsP5wDi9tB9xr6gjZvvWXTR7lDdIsg 213 | QIFc+JGwK4PAsquG2fph2+//aVHqsEVw+5/JhGztDE+1/MU4+ypwW2glWWSdmwZk 214 | rbyKgHCpUnI1fslzcaLMh+9zbWlmXvJVTv4zTuu7D27RwBM11WsP0ZJTCIDBxpEp 215 | JSlcQ47/TeCySd5wMWk3bCwqDY2yZEKgyTiMMh7bkQp7OHtTwVbC/jdz6oXBf6QJ 216 | ji68kmxGJcpyeSWoKpWyUulS8E4j8Z4Cjg5xkxO11talyVhyLZWv/EhCU6QxUXGs 217 | q072GFYxBfuZKEuUmhctnYQUnKWBx5hHvsapXR8EXUzpy8eCNDdYPrSea8FtokZw 218 | 4mH0PwsCYxi1D6w9ovgiiRO1DG85ozrefvM/6+DFu+8UX8hS04wuvcUAsR4BVLEz 219 | k1QcEga7bkynu6RqAw63KsG7R7ejmRBOYrRsmaBs0Ad+aoLvEwSlt7kBDQRPDEov 220 | AQgAr5dkVer3tYvJ6/vnyp3goDC04eZ8SkgWqomKU9nZRsKQHcgVOOmoo/YR/2+q 221 | GAAdAsca4DMTWgTqRlcVBDRUNaium31tSiJ3bmBSVaoeCeqQAcs/D7tgTnZNDzmq 222 | wdyQx+dZBPkGGXsrfo4GzuPlGJqKz0BonsXjI6yoSTUC45x8y5WDabcAoGkmrbmw 223 | 65Ovqe8eXRz0U5NXKo2ANSCfsICu4lzv0zqNnQ2JmGek9p/T69KuSJLFmIzPrH6Z 224 | 7Gh/mOCkNU31wZcbylPoAGU4tDxWROJUChL6d5UsZJcQVXL1Qycn0hkNAvRXqlP7 225 | ICw63ypY1cXJNXRSniFrIGGxuwARAQABiQIlBBgBAgAPAhsMBQJYn+CRBQkS6yKh 226 | AAoJENtpjXGZJCVgLj4QAJzIcHr14UEplQv2NySNYPVKajCz4elKN/e8BUYWrQpt 227 | jQ8rRc57Bd4UVbWj4wFmCAG6ghf4pOSv9PNXSKkEAlwMoqAfgujxzxDEMcImxHXS 228 | F9uthnzNVK9llkJmdGwdLDfj+bDJeuWbIBvZHt8JQk6TtQlMqokC4ThyxaFcNPma 229 | rMoG5VynwDFOHCKKIqkmpKMaZg/BA0Dx0sYKUN7htkVk3c/n+zEeizj117a2OyYP 230 | tMHyvDxLG9l8Sd4WBVF5bGOgvKiftmrlfpTRrmVT6CjeZ8hpM+TjldsWl+FBBqSk 231 | 17ox6YfiKn02Az96Jg+1lalqTrJkZ9RfLc9+8V53cz9j0Gb7/KLxlFWVxFBeou7Q 232 | GjZ5MM9I68Z1Hy3jROuBp6kT8qAwOYzGFsmCjm7B51f3mKTAjpyO16aw3cNssECS 233 | 5g3QBMhwTqIHjFpBLZaAXdsar9aTKBvmmTItAPPf7+ujTITpItDcAfx1ow8cdK9P 234 | OuMQNb7RSiuQ4KOLW4mirngWdIpxwtM4w0arnG4ueZ8dcSi1bkSKHKfjhI2AkjJW 235 | 415vBhRhG3Ww0l1nIwCj+UWwGon2eQa3bYTtCaG4tnuRzOXCgOJbPUmKbu2yd3iD 236 | /L2IWEAA37ce96gg9YYHQSTTc9wu8NovK2fRO5BYsKUlbbK5w5OKytXE2tZN4Q5I 237 | =DSRI 238 | -----END PGP PUBLIC KEY BLOCK----- 239 | ``` 240 | -------------------------------------------------------------------------------- /parse.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007, 2008, 2012, 2017 David Shaw 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "sha1.h" 24 | #include "packets.h" 25 | #include "output.h" 26 | #include "parse.h" 27 | 28 | extern int verbose; 29 | extern int ignore_crc_error; 30 | 31 | struct packet * 32 | parse(FILE *input,unsigned char want,unsigned char stop) 33 | { 34 | int byte; 35 | struct packet *packet=NULL; 36 | 37 | while((byte=fgetc(input))!=EOF) 38 | { 39 | unsigned char type; 40 | unsigned int length; 41 | 42 | if(byte&0x80) 43 | { 44 | int tmp; 45 | 46 | type=byte&0x3F; 47 | 48 | /* Old-style packet type */ 49 | if(!(byte&0x40)) 50 | type>>=2; 51 | 52 | if(type==stop) 53 | { 54 | ungetc(byte,input); 55 | break; 56 | } 57 | 58 | if(byte&0x40) 59 | { 60 | /* New-style packets */ 61 | byte=fgetc(input); 62 | if(byte==EOF) 63 | goto fail; 64 | 65 | if(byte==255) 66 | { 67 | /* 4-byte length */ 68 | tmp=fgetc(input); 69 | if(tmp==EOF) 70 | goto fail; 71 | length=tmp<<24; 72 | tmp=fgetc(input); 73 | if(tmp==EOF) 74 | goto fail; 75 | length|=tmp<<16; 76 | tmp=fgetc(input); 77 | if(tmp==EOF) 78 | goto fail; 79 | length|=tmp<<8; 80 | tmp=fgetc(input); 81 | if(tmp==EOF) 82 | goto fail; 83 | length|=tmp; 84 | } 85 | else if(byte>=224) 86 | { 87 | /* Partial body length, so fail (keys can't use 88 | partial body) */ 89 | fprintf(stderr,"Invalid partial packet encoding\n"); 90 | goto fail; 91 | } 92 | else if(byte>=192) 93 | { 94 | /* 2-byte length */ 95 | tmp=fgetc(input); 96 | if(tmp==EOF) 97 | goto fail; 98 | length=((byte-192)<<8)+tmp+192; 99 | } 100 | else 101 | length=byte; 102 | } 103 | else 104 | { 105 | /* Old-style packets */ 106 | switch(byte&0x03) 107 | { 108 | case 0: 109 | /* 1-byte length */ 110 | byte=fgetc(input); 111 | if(byte==EOF) 112 | goto fail; 113 | length=byte; 114 | break; 115 | 116 | case 1: 117 | /* 2-byte length */ 118 | byte=fgetc(input); 119 | if(byte==EOF) 120 | goto fail; 121 | tmp=fgetc(input); 122 | if(tmp==EOF) 123 | goto fail; 124 | length=byte<<8; 125 | length|=tmp; 126 | break; 127 | 128 | case 2: 129 | /* 4-byte length */ 130 | tmp=fgetc(input); 131 | if(tmp==EOF) 132 | goto fail; 133 | length=tmp<<24; 134 | tmp=fgetc(input); 135 | if(tmp==EOF) 136 | goto fail; 137 | length|=tmp<<16; 138 | tmp=fgetc(input); 139 | if(tmp==EOF) 140 | goto fail; 141 | length|=tmp<<8; 142 | tmp=fgetc(input); 143 | if(tmp==EOF) 144 | goto fail; 145 | length|=tmp; 146 | break; 147 | 148 | default: 149 | fprintf(stderr,"Error: unable to parse old-style length\n"); 150 | goto fail; 151 | } 152 | } 153 | 154 | if(verbose>1) 155 | fprintf(stderr,"Found packet of type %d, length %d\n",type,length); 156 | } 157 | else 158 | { 159 | fprintf(stderr,"Error: unable to parse OpenPGP packets" 160 | " (is this armored data?)\n"); 161 | goto fail; 162 | } 163 | 164 | if(want==0 || type==want) 165 | { 166 | packet=xmalloc(sizeof(*packet)); 167 | packet->type=type; 168 | packet->buf=xmalloc(length); 169 | packet->len=length; 170 | packet->size=length; 171 | if(fread(packet->buf,1,packet->len,input)len) 172 | { 173 | fprintf(stderr,"Short read on packet type %d\n",type); 174 | goto fail; 175 | } 176 | break; 177 | } 178 | else 179 | { 180 | /* We don't want it, so skip the packet. We don't use fseek 181 | here since the input might be on stdin and that isn't 182 | seekable. */ 183 | 184 | size_t i; 185 | 186 | for(i=0;ibuf[0]==3) 202 | { 203 | return -1; 204 | } 205 | else if(packet->buf[0]==4) 206 | { 207 | struct sha1_ctx sha; 208 | unsigned char head[3]; 209 | 210 | sha1_init_ctx(&sha); 211 | 212 | head[0]=0x99; 213 | head[1]=public_len>>8; 214 | head[2]=public_len&0xFF; 215 | 216 | sha1_process_bytes(head,3,&sha); 217 | sha1_process_bytes(packet->buf,public_len,&sha); 218 | sha1_finish_ctx(&sha,fingerprint); 219 | } 220 | 221 | return 0; 222 | } 223 | 224 | #define MPI_LENGTH(_start) (((((_start)[0]<<8 | (_start)[1]) + 7) / 8) + 2) 225 | 226 | ssize_t 227 | extract_secrets(struct packet *packet) 228 | { 229 | size_t offset; 230 | 231 | if(packet->len==0) 232 | return -1; 233 | 234 | /* Secret keys consist of a public key with some secret material 235 | stuck on the end. To get to the secrets, we have to skip the 236 | public stuff. */ 237 | 238 | if(packet->buf[0]==3) 239 | { 240 | fprintf(stderr,"Version 3 (PGP 2.x style) keys are not supported.\n"); 241 | return -1; 242 | } 243 | else if(packet->buf[0]==4) 244 | { 245 | /* Jump 5 bytes in. That gets us past 1 byte of version, and 4 246 | bytes of timestamp. */ 247 | 248 | offset=5; 249 | } 250 | else 251 | return -1; 252 | 253 | if(packet->len<=offset) 254 | return -1; 255 | 256 | switch(packet->buf[offset++]) 257 | { 258 | case 1: /* RSA */ 259 | /* Skip 2 MPIs */ 260 | offset+=MPI_LENGTH(&packet->buf[offset]); 261 | if(packet->len<=offset) 262 | return -1; 263 | offset+=MPI_LENGTH(&packet->buf[offset]); 264 | if(packet->len<=offset) 265 | return -1; 266 | break; 267 | 268 | case 16: /* Elgamal */ 269 | /* Skip 3 MPIs */ 270 | offset+=MPI_LENGTH(&packet->buf[offset]); 271 | if(packet->len<=offset) 272 | return -1; 273 | offset+=MPI_LENGTH(&packet->buf[offset]); 274 | if(packet->len<=offset) 275 | return -1; 276 | offset+=MPI_LENGTH(&packet->buf[offset]); 277 | if(packet->len<=offset) 278 | return -1; 279 | break; 280 | 281 | case 17: /* DSA */ 282 | /* Skip 4 MPIs */ 283 | offset+=MPI_LENGTH(&packet->buf[offset]); 284 | if(packet->len<=offset) 285 | return -1; 286 | offset+=MPI_LENGTH(&packet->buf[offset]); 287 | if(packet->len<=offset) 288 | return -1; 289 | offset+=MPI_LENGTH(&packet->buf[offset]); 290 | if(packet->len<=offset) 291 | return -1; 292 | offset+=MPI_LENGTH(&packet->buf[offset]); 293 | if(packet->len<=offset) 294 | return -1; 295 | break; 296 | 297 | case 18: /* ECDH */ 298 | /* Skip the curve ID and its length byte, plus an MPI, plus the 299 | KDF parameters and their length byte */ 300 | offset+=packet->buf[offset]+1; 301 | if(packet->len<=offset) 302 | return -1; 303 | offset+=MPI_LENGTH(&packet->buf[offset]); 304 | if(packet->len<=offset) 305 | return -1; 306 | offset+=packet->buf[offset]+1; 307 | if(packet->len<=offset) 308 | return -1; 309 | break; 310 | 311 | case 19: /* ECDSA */ 312 | case 22: /* EdDSA - note that this is from an expired draft 313 | https://tools.ietf.org/html/draft-koch-eddsa-for-openpgp-04, 314 | but GnuPG is using algorithm 22 for it. */ 315 | /* Skip the curve ID and its length byte, plus an MPI */ 316 | offset+=packet->buf[offset]+1; 317 | if(packet->len<=offset) 318 | return -1; 319 | offset+=MPI_LENGTH(&packet->buf[offset]); 320 | if(packet->len<=offset) 321 | return -1; 322 | break; 323 | 324 | default: 325 | /* What algorithm? */ 326 | fprintf(stderr,"Unable to parse algorithm %u\n",packet->buf[offset-1]); 327 | return -1; 328 | } 329 | 330 | return offset; 331 | } 332 | 333 | struct packet * 334 | read_secrets_file(FILE *secrets,enum data_type input_type) 335 | { 336 | struct packet *packet=NULL; 337 | int final_crc=0; 338 | unsigned long my_crc=0; 339 | 340 | if(input_type==RAW) 341 | { 342 | unsigned char buffer[1024]; 343 | size_t got; 344 | 345 | while((got=fread(buffer,1,1024,secrets))) 346 | packet=append_packet(packet,buffer,got); 347 | 348 | if(got==0 && !feof(secrets)) 349 | { 350 | fprintf(stderr,"Error: unable to read secrets file\n"); 351 | free_packet(packet); 352 | return NULL; 353 | } 354 | 355 | if(packet->len>=3) 356 | { 357 | /* Grab the last 3 bytes to be the CRC24 */ 358 | my_crc =packet->buf[packet->len-3]<<16; 359 | my_crc|=packet->buf[packet->len-2]<<8; 360 | my_crc|=packet->buf[packet->len-1]; 361 | final_crc=1; 362 | packet->len-=3; 363 | } 364 | } 365 | else 366 | { 367 | char line[1024]; 368 | unsigned int next_linenum=1; 369 | 370 | while(fgets(line,1024,secrets)) 371 | { 372 | unsigned int linenum,did_digit=0; 373 | unsigned long line_crc=CRC24_INIT; 374 | char *tok; 375 | 376 | if(line[0]=='#' || line[0]=='\n' || line[0]=='\r') 377 | continue; 378 | 379 | linenum=atoi(line); 380 | if(linenum!=next_linenum) 381 | { 382 | fprintf(stderr,"Error: missing line number %u (saw %u)\n", 383 | next_linenum,linenum); 384 | free_packet(packet); 385 | return NULL; 386 | } 387 | else 388 | next_linenum=linenum+1; 389 | 390 | tok=strchr(line,':'); 391 | if(tok) 392 | { 393 | tok=strchr(tok,' '); 394 | 395 | while(tok) 396 | { 397 | char *next; 398 | 399 | while(*tok==' ') 400 | tok++; 401 | 402 | next=strchr(tok,' '); 403 | 404 | if(next==NULL) 405 | { 406 | /* End of line, so check the CRC. */ 407 | unsigned long new_crc; 408 | 409 | if(sscanf(tok,"%06lX",&new_crc)) 410 | { 411 | if(did_digit) 412 | { 413 | if((new_crc&0xFFFFFFL)!=(line_crc&0xFFFFFFL)) 414 | { 415 | fprintf(stderr,"CRC on line %d does not" 416 | " match (%06lX!=%06lX)\n",linenum, 417 | new_crc&0xFFFFFFL, 418 | line_crc&0xFFFFFFL); 419 | if(!ignore_crc_error) 420 | { 421 | free_packet(packet); 422 | return NULL; 423 | } 424 | } 425 | } 426 | else 427 | { 428 | final_crc=1; 429 | my_crc=new_crc; 430 | } 431 | } 432 | } 433 | else 434 | { 435 | unsigned int digit; 436 | 437 | if(sscanf(tok,"%02X",&digit)) 438 | { 439 | unsigned char d=digit; 440 | packet=append_packet(packet,&d,1); 441 | do_crc24(&line_crc,&d,1); 442 | did_digit=1; 443 | } 444 | } 445 | 446 | tok=next; 447 | } 448 | } 449 | else 450 | { 451 | fprintf(stderr,"No colon ':' found in line %u\n",linenum); 452 | free_packet(packet); 453 | return NULL; 454 | } 455 | } 456 | } 457 | 458 | if(final_crc) 459 | { 460 | unsigned long all_crc=CRC24_INIT; 461 | 462 | do_crc24(&all_crc,packet->buf,packet->len); 463 | 464 | if((my_crc&0xFFFFFFL)!=(all_crc&0xFFFFFFL)) 465 | { 466 | fprintf(stderr,"CRC of secret does not match (%06lX!=%06lX)\n", 467 | my_crc&0xFFFFFFL,all_crc&0xFFFFFFL); 468 | if(!ignore_crc_error) 469 | { 470 | free_packet(packet); 471 | return NULL; 472 | } 473 | } 474 | } 475 | else 476 | { 477 | fprintf(stderr,"CRC of secret is missing\n"); 478 | if(!ignore_crc_error) 479 | { 480 | free_packet(packet); 481 | return NULL; 482 | } 483 | } 484 | 485 | return packet; 486 | } 487 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Library General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License 307 | along with this program; if not, write to the Free Software 308 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 309 | 310 | 311 | Also add information on how to contact you by electronic and paper mail. 312 | 313 | If the program is interactive, make it output a short notice like this 314 | when it starts in an interactive mode: 315 | 316 | Gnomovision version 69, Copyright (C) year name of author 317 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 318 | This is free software, and you are welcome to redistribute it 319 | under certain conditions; type `show c' for details. 320 | 321 | The hypothetical commands `show w' and `show c' should show the appropriate 322 | parts of the General Public License. Of course, the commands you use may 323 | be called something other than `show w' and `show c'; they could even be 324 | mouse-clicks or menu items--whatever suits your program. 325 | 326 | You should also get your employer (if you work as a programmer) or your 327 | school, if any, to sign a "copyright disclaimer" for the program, if 328 | necessary. Here is a sample; alter the names: 329 | 330 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 331 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 332 | 333 | , 1 April 1989 334 | Ty Coon, President of Vice 335 | 336 | This General Public License does not permit incorporating your program into 337 | proprietary programs. If your program is a subroutine library, you may 338 | consider it more useful to permit linking proprietary applications with the 339 | library. If this is what you want to do, use the GNU Library General 340 | Public License instead of this License. 341 | --------------------------------------------------------------------------------