//--------------------------------------------------------------------------- // Copyright (C) 2000 Dallas Semiconductor Corporation, All Rights Reserved. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // IN NO EVENT SHALL DALLAS SEMICONDUCTOR BE LIABLE FOR ANY CLAIM, DAMAGES // OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // // Except as contained in this notice, the name of Dallas Semiconductor // shall not be used except as stated in the Dallas Semiconductor // Branding Policy. //--------------------------------------------------------------------------- // // shaibutton.c - Protocol-level functions as well as useful utility // functions for sha applications. // // Version: 2.10 // #include "ownet.h" #include "shaibutton.h" //Global SMALLINT in_overdrive[MAX_PORTNUM]; //--------------------------------------------------------------------- // Uses File I/O API to find the coprocessor with a specific // coprocessor file name. Usually 'COPR.0'. // // 'copr' - Structure for holding coprocessor information // 'fe' - pointer to file entry structure, with proper filename. // // Returns: TRUE, found a valid coprocessor // FALSE, no coprocessor is present // SMALLINT FindCoprSHA(SHACopr* copr, FileEntry* fe) { SMALLINT FoundCopr = FALSE; // now get all the SHA iButton parts until we find // one that has the right file on it. if(FindNewSHA(copr->portnum, copr->devAN, TRUE)) { do { short handle; if(owOpenFile(copr->portnum, copr->devAN, fe, &handle)) { int length = fe->NumPgs << 5; uchar* raw = malloc(length*5); if(owReadFile(copr->portnum, copr->devAN, handle, raw, length, &length)) { owCloseFile(copr->portnum, copr->devAN, handle); GetCoprFromRawData(copr, raw, length); FoundCopr = TRUE; } free(raw); } } while(!FoundCopr && FindNewSHA(copr->portnum, copr->devAN, FALSE)); } return FoundCopr; } //--------------------------------------------------------------------- // Extracts coprocessor configuration information from raw data. // // 'copr' - Structure for holding coprocessor information // 'raw' - Raw bytes, usually from file stored on disk or on the // coprocessor itself. // 'length' - The length of coprocessor data. // void GetCoprFromRawData(SHACopr* copr, uchar* raw, int length) { int i, namelen, siglen, auxlen; //copy in the name of the user's service file memcpy(copr->serviceFilename, raw, 5); //various page numbers copr->signPageNumber = raw[5]; copr->authPageNumber = raw[6]; copr->wspcPageNumber = raw[7]; //version information copr->versionNumber = raw[8]; //skip 4 bytes for date info; //get bind data, bind code, and signing challenge memcpy(copr->bindData, &raw[13], 32); memcpy(copr->bindCode, &raw[45], 7); memcpy(copr->signChlg, &raw[52], 3); namelen = raw[55]; siglen = raw[56]; auxlen = raw[57]; // read in provider name as null-terminated string copr->providerName = malloc(namelen+1); memcpy(copr->providerName, &raw[58], namelen); copr->providerName[namelen] = '\0'; // get initial signature memcpy(copr->initSignature, &raw[58+namelen], (siglen>20?20:siglen) ); // read in auxilliary data as null-terminated string copr->auxilliaryData = malloc(auxlen+1); memcpy(copr->auxilliaryData, &raw[58+namelen+siglen], auxlen); copr->auxilliaryData[auxlen] = '\0'; //encryption code i = 58+namelen+siglen+auxlen; copr->encCode = raw[i]; //ds1961S compatibility flag - optional if((length>i+1) && (raw[i+1]!=0)) copr->ds1961Scompatible = TRUE; else copr->ds1961Scompatible = FALSE; } //--------------------------------------------------------------------- // Uses File I/O API to find the user token with a specific // service file name. Usually 'DSLM.102'. // // 'user' - Structure for holding user token information // 'fe' - pointer to file entry structure, with proper // service filename. // 'doBlocking' - if TRUE, method blocks until a user token is found. // // Returns: TRUE, found a valid user token // FALSE, no user token is present // SMALLINT FindUserSHA(SHAUser* user, FileEntry* fe, SMALLINT doBlocking) { SMALLINT FoundUser = FALSE; for(;;) { // now get all the SHA iButton parts until we find // one that has the right file on it. while(!FoundUser && FindNewSHA(user->portnum, user->devAN, FALSE)) { short handle; if(owOpenFile(user->portnum, user->devAN, fe, &handle)) { user->accountPageNumber = fe->Spage; FoundUser = TRUE; owCloseFile(user->portnum, user->devAN, handle); } else { PrintSerialNum(user->devAN); printf(" is not a SHA User."); } //if(user->devAN[7]==0x88) //{ // user->accountPageNumber = 8; // FoundUser = TRUE; //} } if(FoundUser) return TRUE; else if(!doBlocking) return FALSE; } } //final buffer for tracking list of known SHA parts //only holds the CRC for each part static uchar ListOfKnownSHA[MAX_PORTNUM][MAX_SHA_IBUTTONS]; static int listIndex = 0; //intermediate buffer for tracking list of known SHA parts static uchar listBuffer[MAX_PORTNUM][MAX_SHA_IBUTTONS]; static int indexBuffer = 0; //holds last state of FindNewSHA static SMALLINT needsFirst = FALSE; //--------------------------------------------------------------------- // Finds new SHA iButtons on the given port. Uses 'triple-buffer' // technique to insure it only finds 'new' buttons. // // 'portnum' - number 0 to MAX_PORTNUM-1. This number is provided to // indicate the symbolic port number. // 'devAN' - pointer to buffer for device address // 'resetList' - if TRUE, final buffer is cleared. // // Returns: TRUE, found a new SHA iButton. // FALSE, no new buttons are present. // SMALLINT FindNewSHA(int portnum, uchar* devAN, SMALLINT resetList) { uchar ROM[8]; uchar tempList[MAX_SHA_IBUTTONS]; int tempIndex = 0, i; SMALLINT hasDevices = TRUE, completeList = FALSE; // force back to standard speed owSpeed(portnum,MODE_NORMAL); in_overdrive[portnum] = FALSE; // get first device in list with the specified family if(needsFirst || resetList) { hasDevices = owFirst(portnum, TRUE, FALSE); completeList = TRUE; if(resetList) listIndex = 0; } else hasDevices = owNext(portnum, TRUE, FALSE); if(hasDevices) { do { // verify correct device type owSerialNum(portnum, ROM, TRUE); tempList[tempIndex++] = ROM[7]; // compare crc to complete list of ROMs for(i=0; i0 && offset<17) start += offset; //set the serial number owSerialNum(copr->portnum, copr->devAN, FALSE); OWASSERT( EraseScratchpadSHA(copr->portnum, addr, FALSE), OWERROR_ERASE_SCRATCHPAD_FAILED, FALSE ); OWASSERT( SHAFunction(copr->portnum, SHA_COMPUTE_CHALLENGE, addr, TRUE), OWERROR_SHA_FUNCTION_FAILED, FALSE ); OWASSERT( ReadScratchpadSHA(copr->portnum, &addr, &es, scratchpad, TRUE), OWERROR_READ_SCRATCHPAD_FAILED, FALSE ); memcpy(chlg,&scratchpad[start],3); return TRUE; } //------------------------------------------------------------------------- // Answers a random challenge by performing an authenticated read of the // user's account information. // // 'user' - Structure for holding user token information. // 'chlg' - 3-byte buffer of challenge data. // // Return: the value of the write cycle counter for the account page. // int AnswerChallenge(SHAUser* user, uchar* chlg) { int addr = user->accountPageNumber << 5; user->writeCycleCounter = -1; memcpy(&user->accountFile.raw[20], chlg, 3); //set the serial number owSerialNum(user->portnum, user->devAN, FALSE); if(user->devAN[0]==0x18) { // for the DS1963S OWASSERT( EraseScratchpadSHA(user->portnum, addr, FALSE), OWERROR_ERASE_SCRATCHPAD_FAILED, FALSE ); OWASSERT( WriteScratchpadSHA(user->portnum, addr, user->accountFile.raw, 32, TRUE), OWERROR_WRITE_SCRATCHPAD_FAILED, FALSE ); user->writeCycleCounter = ReadAuthPageSHA(user->portnum, user->accountPageNumber, user->accountFile.raw, user->responseMAC, TRUE); } else if(user->devAN[0]==0x33) { // for the DS1961S OWASSERT( WriteScratchpadSHA33(user->portnum, addr, &user->accountFile.raw[16], FALSE), OWERROR_WRITE_SCRATCHPAD_FAILED, FALSE ); user->writeCycleCounter = ReadAuthPageSHA33(user->portnum, user->accountPageNumber, user->accountFile.raw, user->responseMAC, TRUE); } return user->writeCycleCounter; } //------------------------------------------------------------------------- // Verifies the authentication response of a user token. // // 'copr' - Structure for holding coprocessor information. // 'user' - Structure for holding user token information. // 'chlg' - 3-byte buffer of challenge data. // 'doBind' - if true, the user's unique secret is recreated on the // coprocessor. If this function is called multiple times, // it is acceptable to skip the bind for all calls after // the first on the same user token. // // Return: If TRUE, the user's authentication response matched exactly the // signature generated by the coprocessor. // If FALSE, an error occurred or the signature did not match. // SMALLINT VerifyAuthResponse(SHACopr* copr, SHAUser* user, uchar* chlg, SMALLINT doBind) { int addr = copr->wspcPageNumber << 5; int wcc = user->writeCycleCounter; uchar sign_cmd; uchar scratchpad[32]; uchar fullBindCode[15]; memset(scratchpad, 0x00, 32); memset(fullBindCode, 0x0FF, 15); // format the bind code properly if(user->devAN[0]==0x18) { // Format for DS1963S memcpy(fullBindCode, copr->bindCode, 4); memcpy(&fullBindCode[12], &(copr->bindCode[4]), 3); // use ValidateDataPage command sign_cmd = SHA_VALIDATE_DATA_PAGE; // copy wcc LSB first IntToBytes(&scratchpad[8], 4, wcc); } else if(user->devAN[0]==0x33) { // Leave bindCode FF for DS1961S // Use AuthenticateHost command sign_cmd = SHA_AUTHENTICATE_HOST; // the user doesn't have a write cycle counter memset(&scratchpad[8], 0x0FF, 4); } else { OWERROR(OWERROR_WRONG_TYPE); return FALSE; } // the pagenumber fullBindCode[4] = (uchar)user->accountPageNumber; // and 7 bytes of the address of current device memcpy(&fullBindCode[5], user->devAN, 7); // get the user address and page num from fullBindCode memcpy(&scratchpad[12], &fullBindCode[4], 8); // set the same challenge bytes memcpy(&scratchpad[20], chlg, 3); // set the serial number to that of the coprocessor owSerialNum(copr->portnum, copr->devAN, FALSE); // install user's unique secret on the wspc secret if(doBind) { OWASSERT( BindSecretToiButton(copr->portnum, copr->authPageNumber, copr->wspcPageNumber&7, copr->bindData, fullBindCode, FALSE), OWERROR_BIND_SECRET_FAILED, FALSE ); if(user->devAN[0]==0x33) { // also copy the resulting secret into secret location 0, to // replace the signing secret. Necessary for producing the // DS1961S's write-authorization MAC. OWASSERT( CopySecretSHA(copr->portnum, 0), OWERROR_COPY_SECRET_FAILED, FALSE); } } // recreate the signature and verify OWASSERT( WriteDataPageSHA(copr->portnum, copr->wspcPageNumber, user->accountFile.raw, doBind), OWERROR_WRITE_DATA_PAGE_FAILED, FALSE ); OWASSERT( WriteScratchpadSHA(copr->portnum, addr, scratchpad, 32, TRUE), OWERROR_WRITE_SCRATCHPAD_FAILED, FALSE ); OWASSERT( SHAFunction(copr->portnum, sign_cmd, addr, TRUE), OWERROR_SHA_FUNCTION_FAILED, FALSE ); OWASSERT( MatchScratchpadSHA(copr->portnum, user->responseMAC, TRUE), OWERROR_MATCH_SCRATCHPAD_FAILED, FALSE ); return TRUE; } //------------------------------------------------------------------------- // Creates a data signature for the given data buffer using the hardware // coprocessor. // // 'copr' - Structure for holding coprocessor information. // 'data' - data written to the data page to sign // 'scratchpad' - data written to the scratchpad to sign // 'signature' - data buffer which is either holding the signature that // must match exactly what is generated on the coprocessor // -or- will hold the resulting signature created by the // coprocessor. // 'readSignature' - implies whether or not the signature buffer // receives the contents of the scratchpad or is used to // match the contents of the scratchpad. If true, // scratchpad contents are read into the signature buffer. // // Return: If TRUE, the user's authentication response matched exactly the // signature generated by the coprocessor or the signature was // successfully copied into the return buffer. // If FALSE, an error occurred or the signature did not match. // SMALLINT CreateDataSignature(SHACopr* copr, uchar* data, uchar* scratchpad, uchar* signature, SMALLINT readSignature) { int addr = copr->signPageNumber << 5; // set the serial number to that of the coprocessor owSerialNum(copr->portnum, copr->devAN, FALSE); OWASSERT( WriteDataPageSHA(copr->portnum, copr->signPageNumber, data, FALSE), OWERROR_WRITE_DATA_PAGE_FAILED, FALSE ); OWASSERT( WriteScratchpadSHA(copr->portnum, addr, scratchpad, 32, TRUE), OWERROR_WRITE_SCRATCHPAD_FAILED, FALSE ); OWASSERT( SHAFunction(copr->portnum, SHA_SIGN_DATA_PAGE, addr, TRUE), OWERROR_SHA_FUNCTION_FAILED, FALSE ); if(readSignature) { OWASSERT( ReadScratchpadSHA(copr->portnum, 0, 0, scratchpad, TRUE), OWERROR_READ_SCRATCHPAD_FAILED, FALSE ); } else { OWASSERT( MatchScratchpadSHA(copr->portnum, signature, TRUE), OWERROR_MATCH_SCRATCHPAD_FAILED, FALSE ); } memcpy(signature, &scratchpad[8], 20); return TRUE; } //------------------------------------------------------------------------- // Decipher the variable Val into a 'len' byte array to set // LSB First // SMALLINT IntToBytes(uchar* byteArray, int len, unsigned int val) { int i = 0; for (; i>= 8; } if(val==0) return TRUE; else return FALSE; } //------------------------------------------------------------------------- // Decipher the range 'len' of the byte array to an integer // LSB First // int BytesToInt(uchar* byteArray, int len) { unsigned int val = 0; int i = (len - 1); // Concatanate the byte array into one variable. for (; i >= 0; i--) { val <<= 8; val |= (byteArray[i] & 0x00FF); } return val; }