/*************************************************************************** $RCSfile: rsacard.cpp,v $ ------------------- cvs : $Id: rsacard.cpp,v 1.5 2003/05/08 11:01:24 aquamaniac Exp $ begin : Fri Feb 07 2003 copyright : (C) 2003 by Martin Preuss email : martin@libchipcard.de **************************************************************************** * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * ****************************************************************************/ #ifdef HAVEN_CONFIG_H # include #endif /* Internationalization */ #ifdef HAVE_GETTEXT_ENVIRONMENT # include # include # define I18N(m) gettext(m) #else # define I18N(m) m #endif #define I18NT(m) m #include #include #include #include #include #define k_PRG "rsacard" #define k_PRG_VERSION_INFO \ "RSACard v0.1 (part of libchipcard v"k_CHIPCARD_VERSION_STRING")\n"\ "(c) 2002 Martin Preuss\n" \ "This program is free software licensed under GPL.\n"\ "See COPYING for details.\n" void usage(string name) { fprintf(stderr,"%s%s%s", I18N("RSACard - A cryptographic tool for RSA cards\n" "(c) 2002 Martin Preuss\n" "This library is free software; you can redistribute it and/or\n" "modify it under the terms of the GNU Lesser General Public\n" "License as published by the Free Software Foundation; either\n" "version 2.1 of the License, or (at your option) any later version.\n" "\n" "Usage:\n" "%s COMMAND [-C CONFIGFILE]\n" " COMMAND\n" " sign : sign data\n" " verify : verify a signature\n" " encrypt : encrypt data\n" " decrypt : decrypt data\n" " create : create keys\n" "\n" " OPTIONS\n" " -C CONFIGFILE - configuration file of libchipcard to be used\n" " --logfile FILE - use given FILE as log file\n" " --logtype TYPE - use given TYPE as log type\n" " These are the valid types:\n" " stderr (log to standard error channel)\n" " file (log to the file given by --logfile)\n"), #ifdef HAVE_SYSLOG_H I18N(" syslog (log via syslog)\n"), #else "", #endif I18N(" Default is stderr\n" " --loglevel LEVEL - set the loglevel\n" " Valid levels are:\n" " emergency, alert, critical, error,\n" " warning, notice, info and debug\n" " Default is \"warning\".\n" " --indata - specify the input data file\n" " --signature - specify the signature file (in or out)\n" " --outdata - specify the output data file\n" " -i KEYNUM - use the given keypair (1-5)\n" " --peer - use the peers keys. If omitted the users\n" " own keys are used. Use this option if\n" " you want to verify a signature created by\n" " the peer whose public keys are stored on\n" " the card.\n" " -k - use secure pin input\n" " -p PIN - specify the cardholder pin to use\n") , name.c_str()); } struct s_args { string configFile; // -C string logFile; // --logfile LOGGER_LOGTYPE logType; // --logtype LOGGER_LEVEL logLevel; // --loglevel string inFile; // --indata string outFile; // --outdata string signFile; // --signature bool secureInput; // -k bool peerKey; // --peer string pin; // -p int index; // -i list params; }; int checkArgs(s_args &args, int argc, char **argv) { int i; string tmp; i=1; args.configFile=CHIPCARDC_CFGFILE; args.logFile="rsacard.log"; args.logType=LoggerTypeConsole; args.logLevel=LoggerLevelWarning; args.secureInput=false; args.index=0; args.peerKey=false; while (i=argc) return 1; args.configFile=argv[i]; } else if (tmp=="--logfile") { i++; if (i>=argc) return 1; args.logFile=argv[i]; } else if (tmp=="--logtype") { i++; if (i>=argc) return 1; if (argv[i]=="stderr") args.logType=LoggerTypeConsole; else if (argv[i]=="file") args.logType=LoggerTypeFile; #ifdef HAVE_SYSLOG_H else if (argv[i]=="syslog") args.logType=LoggerTypeSyslog; #endif else { fprintf(stderr,I18N("Unknown log type \"%s\"\n"), argv[i]); return 1; } } else if (tmp=="--loglevel") { i++; if (i>=argc) return 1; if (strcmp(argv[i], "emergency")==0) args.logLevel=LoggerLevelEmergency; else if (strcmp(argv[i], "alert")==0) args.logLevel=LoggerLevelAlert; else if (strcmp(argv[i], "critical")==0) args.logLevel=LoggerLevelCritical; else if (strcmp(argv[i], "error")==0) args.logLevel=LoggerLevelError; else if (strcmp(argv[i], "warning")==0) args.logLevel=LoggerLevelWarning; else if (strcmp(argv[i], "notice")==0) args.logLevel=LoggerLevelNotice; else if (strcmp(argv[i], "info")==0) args.logLevel=LoggerLevelInfo; else if (strcmp(argv[i], "debug")==0) args.logLevel=LoggerLevelDebug; else { fprintf(stderr, I18N("Unknown log level \"%s\"\n"), argv[i]); return 1; } } else if (tmp=="--indata") { i++; if (i>=argc) return 1; args.inFile=argv[i]; } else if (tmp=="--outdata") { i++; if (i>=argc) return 1; args.outFile=argv[i]; } else if (tmp=="--signature") { i++; if (i>=argc) return 1; args.signFile=argv[i]; } else if (tmp=="-k") { args.secureInput=true; } else if (tmp=="--peer") { args.peerKey=true; } else if (tmp=="-p") { i++; if (i>=argc) return 1; args.pin=argv[i]; } else if (tmp=="-i") { i++; if (i>=argc) return 1; args.index=atoi(argv[i]); } // other options to be inserted here else if (tmp=="-h" || tmp=="--help") { usage(argv[0]); return -1; } else if (tmp=="-V" || tmp=="--version") { fprintf(stdout,k_PRG_VERSION_INFO); return -1; } else // otherwise add param args.params.push_back(tmp); i++; } // while // that's it return 0; } int hashFile(const string &filename, string &hash) { unsigned char hashbuffer[20]; FILE *f; unsigned char buffer[1024]; string data; int rc; int t; char errbuff[256]; CRYP_RMD160 *rmd; // open file f=fopen(filename.c_str(),"rb"); if (!f) { fprintf(stderr, I18N("Could not open input file: %s\n"), strerror(errno)); return 2; } // read file and generate hash rmd=Cryp_RMD160_new(); rc=Cryp_RMD160_Init(rmd); if (!Error_IsOk(rc)) { Error_ToString(rc,errbuff, sizeof(errbuff)); \ fprintf(stderr, I18N("ERROR: Could not create RMD160 context (%s)\n"), errbuff); Cryp_RMD160_free(rmd); return 3; } while (!feof(f)) { // size not given, read as much as possible t=sizeof(buffer); // read from file t=fread((char*)buffer,1,t,f); if (t<1) { fprintf(stderr, I18N("ERROR: Could not read from file. (%s)\n"), strerror(errno)); Cryp_RMD160_free(rmd); return 2; } rc=Cryp_RMD160_Update(rmd, buffer, t); if (!Error_IsOk(rc)) { Error_ToString(rc,errbuff, sizeof(errbuff)); \ fprintf(stderr, I18N("ERROR: Could not digest data (%s)\n"), errbuff); Cryp_RMD160_free(rmd); return 3; } } // while // get hash t=sizeof(hashbuffer); rc=Cryp_RMD160_Final(rmd, hashbuffer, &t); Cryp_RMD160_free(rmd); if (!Error_IsOk(rc)) { Error_ToString(rc,errbuff, sizeof(errbuff)); \ fprintf(stderr, I18N("ERROR: Could not get hash (%s)\n"), errbuff); return 3; } hash=string((char*)hashbuffer, sizeof(hashbuffer)); // close file if (fclose(f)) { fprintf(stderr, I18N("Could not close input file: %s\n"), strerror(errno)); return 2; } return 0; } int openCard(CTPointer card) { unsigned int rflags; unsigned int rstatus; CTError err; fprintf(stderr,"Please insert your card into any reader\n"); rstatus=CHIPCARD_STATUS_INSERTED | CHIPCARD_STATUS_PROCESSOR; rflags=0; err=card.ref().getCard(15, false, rflags, 0, rstatus, CHIPCARD_STATUS_INSERTED| CHIPCARD_STATUS_PROCESSOR| CHIPCARD_STATUS_LOCKED_BY_OTHER); if (!err.isOk()) { if (err.code()==k_CTERROR_API && (err.subcode1()==CHIPCARD_ERROR_NO_TRANSPORT || err.subcode1()==CHIPCARD_ERROR_NO_REQUEST)) { fprintf(stderr, "Service unreachable, maybe \"chipcardd\" is not running?\n" ); return 2; } fprintf(stderr,"No card inserted within some seconds, aborting.\n"); return 2; } fprintf(stderr,"Card is inserted, working.\n"); err=card.ref().openCard(); if (!err.isOk()) { fprintf(stderr,"%s\n",err.errorString().c_str()); return 3; } return 0; } bool _checkPinError(CTError err) { if (!err.isOk()) { if (err.code()==0) { if (err.subcode1()==0x63 && err.subcode2()>=0xc0) { switch(err.subcode2()-0xc0) { case 0: fprintf(stderr, "VERY SERIOUS WARNING:\n" "=====================\n" "You already entered a false pin 3 times !\n" "If you enter a bad pin again " "then your card gets blocked!\n" "Please use now the last program which allowed you " "to successfully enter the pin in order to reset the\n" "error counter !\n"); break; case 1: fprintf(stderr, "SERIOUS WARNING:\n" "=====================\n" "You already entered a false pin twice !\n" "There is only one bad try left, so please be carefull.\n" "Please use now the last program which allowed you " "to successfully enter the pin in order to reset the\n" "error counter !\n"); break; case 2: default: fprintf(stderr, "WARNING:\n" "=====================\n" "You entered a false pin.\n" "There are only two bad tries left, " "so please be carefull.\n" "If you are very sure that the pin you entered is " "correct \n" "then please stop using this program and contact\n" " martin@libchipcard.de \n" "to report this incidence.\n" "If it is possible that you made a mistake, then please\n" "try again.\n"); break; } // switch } // if bad pin else if (err.subcode1()==0x69 && err.subcode2()>=0x83) fprintf(stderr, "Sorry, but your card is blocked.\n" "You entered a bad pin too often, so for security reasons\n" "the card blocked itself.\n" "Unfortunately you will have to ask your bank for another\n" "card.\n"); } return false; } else return true; } int sign(s_args &args) { CTPointer card; CTError err; int rv; string hash; int kid; int knum; string signature; FILE *f; // first generate hash if (args.inFile.empty()) { fprintf(stderr,"No input file, please use \"--infile FILE\"\n"); return 1; } if (args.signFile.empty()) { fprintf(stderr, "No signature file, please use \"--signature FILE\"\n"); return 1; } rv=hashFile(args.inFile,hash); if (rv!=0) return rv; // let the card sign the hash card=new RSACard(); try { rv=openCard(card); if (rv!=0) return rv; // verify pin if (args.secureInput) { if (card.ref().readerFlags() & CHIPCARD_READERFLAGS_KEYPAD) { fprintf(stderr,"Please enter your pin into the reader's keypad:\n"); err=card.ref().verifyPin(0x90); fprintf(stderr,"Pin entered.\n"); } else { fprintf(stderr, "Your reader has no keypad or it has been disabled.\n" "Please give the pin as an argument to this program.\n"); return 1; } } else { if (args.pin.empty()) { if (card.ref().readerFlags() & CHIPCARD_READERFLAGS_KEYPAD) { fprintf(stderr, "No pin. Your reader seems to have a keypad, so please\n" "use \"-k\" to enter the pin directly into the reader.\n"); return 1; } else { fprintf(stderr, "Please give the pin as an argument to this program.\n"); return 1; } } else err=card.ref().verifyPin(0x90, args.pin); } if (!_checkPinError(err)) { fprintf(stderr,"Could not verify pin: %s\n",err.errorString().c_str()); return 3; } fprintf(stderr,"Pin ok.\n"); // let the card sign the hash if (args.index==0) knum=0; else knum=args.index-1; kid=card.ref().getKeyId(knum,args.peerKey,true); signature=card.ref().sign(kid,hash); } catch(CTError xerr) { fprintf(stderr,"%s\n",xerr.errorString().c_str()); card.ref().closeCard(); return 3; } err=card.ref().closeCard(); if (!err.isOk(0x62)) { fprintf(stderr,"%s\n",err.errorString().c_str()); return 2; } // save signature to file f=fopen(args.signFile.c_str(),"w+"); if (!f) { fprintf(stderr, "Could not create signature file: %s\n", strerror(errno)); return 2; } if (fwrite(signature.c_str(), 1, signature.length(), f)!=signature.length()) { fclose(f); fprintf(stderr, "Could not write to signature file: %s\n", strerror(errno)); return 2; } if (fclose(f)) { fprintf(stderr, "Could not close signature file: %s\n", strerror(errno)); return 2; } return 0; } int verify(s_args &args) { CTPointer card; CTError err; int rv; string hash; int kid; int knum; string signature; unsigned char signbuffer[128]; FILE *f; int i; if (args.inFile.empty()) { fprintf(stderr,"No input file, please use \"--infile FILE\"\n"); return 1; } if (args.signFile.empty()) { fprintf(stderr, "No signature file, please use \"--signature FILE\"\n"); return 1; } // load signature from file f=fopen(args.signFile.c_str(),"r"); if (!f) { fprintf(stderr, "Could not open signature file: %s\n", strerror(errno)); return 2; } i=fread(signbuffer, 1, sizeof(signbuffer), f); if (ferror(f)) { fclose(f); fprintf(stderr, "Could not read from signature file: %s\n", strerror(errno)); return 2; } signature=string((char*)signbuffer, i); if (fclose(f)) { fprintf(stderr, "Could not close signature file: %s\n", strerror(errno)); return 2; } // generate hash for comparison rv=hashFile(args.inFile,hash); if (rv!=0) return rv; // let the card sign the hash card=new RSACard(); try { rv=openCard(card); if (rv!=0) return rv; // verify pin if (args.secureInput) { if (card.ref().readerFlags() & CHIPCARD_READERFLAGS_KEYPAD) { fprintf(stderr,"Please enter your pin into the reader's keypad:\n"); err=card.ref().verifyPin(0x90); fprintf(stderr,"Pin entered.\n"); } else { fprintf(stderr, "Your reader has no keypad or it has been disabled.\n" "Please give the pin as an argument to this program.\n"); return 1; } } else { if (args.pin.empty()) { if (card.ref().readerFlags() & CHIPCARD_READERFLAGS_KEYPAD) { fprintf(stderr, "No pin. Your reader seems to have a keypad, so please\n" "use \"-k\" to enter the pin directly into the reader.\n"); return 1; } else { fprintf(stderr, "Please give the pin as an argument to this program.\n"); return 1; } } else err=card.ref().verifyPin(0x90, args.pin); } if (!_checkPinError(err)) { fprintf(stderr,"Could not verify pin: %s\n",err.errorString().c_str()); return 3; } fprintf(stderr,"Pin ok.\n"); // let the card sign the hash if (args.index==0) knum=0; else knum=args.index-1; kid=card.ref().getKeyId(knum,args.peerKey,true); err=card.ref().verify(kid,hash,signature); if (!err.isOk()) { fprintf(stderr,"ERROR: %s\n", err.errorString().c_str()); card.ref().closeCard(); fprintf(stderr,"Signature is not valid.\n"); return 4; } fprintf(stderr,"Signature is valid.\n"); } catch(CTError xerr) { fprintf(stderr,"%s\n",xerr.errorString().c_str()); card.ref().closeCard(); return 3; } err=card.ref().closeCard(); if (!err.isOk(0x62)) { fprintf(stderr,"%s\n",err.errorString().c_str()); return 2; } return 0; } int main(int argc, char **argv) { s_args args; int rv; string cmd; #ifdef HAVE_GETTEXT_ENVIRONMENT setlocale(LC_ALL,""); if (bindtextdomain("rsacard", I18N_PATH)==0) { fprintf(stderr," Error bindtextdomain()\n"); } if (textdomain("rsacard")==0) { fprintf(stderr," Error textdomain()\n"); } #endif rv=checkArgs(args,argc,argv); if (rv==-1) return 0; else if (rv) return rv; if (args.params.empty()) { usage(argv[0]); return 1; } if (Logger_Open("rsacard", args.logFile.c_str(), args.logType, LoggerFacilityUser)) { fprintf(stderr,"Could not start logging, aborting.\n"); return 2; } Logger_SetLevel(args.logLevel); rv=ChipCard_Init(args.configFile.c_str(),0); if (rv!=CHIPCARD_SUCCESS) { fprintf(stderr, "Error initializing libchipcard (%d), aborting.\n",rv); return 2; } cmd=args.params.front(); if (cmd=="sign") rv=sign(args); else if (cmd=="verify") rv=verify(args); else { fprintf(stderr,"Unknown command.\n"); usage(argv[0]); rv=1; } ChipCard_Fini(); return rv; }