void SleepForUpTicks( uint64_t inTicks ) { uint64_t ticks, deadline; ticks = UpTicks(); deadline = ticks + inTicks; for( ; ticks < deadline; ticks = UpTicks() ) { ticks = deadline - ticks; msleep(ticks); } }
static OSStatus __MFiSAP_Exchange_ServerM1( MFiSAPRef inRef, const uint8_t * inInputPtr, size_t inInputLen, uint8_t ** outOutputPtr, size_t * outOutputLen ) { OSStatus err; const uint8_t * inputEnd; const uint8_t * clientECDHPublicKey; uint8_t ourPrivateKey[ kMFiSAP_ECDHKeyLen ]; uint8_t ourPublicKey[ kMFiSAP_ECDHKeyLen ]; SHA_CTX sha1Context; uint8_t digest[ 20 ]; uint8_t * signaturePtr = NULL; size_t signatureLen; uint8_t * certificatePtr = NULL; size_t certificateLen; uint8_t aesMasterKey[ kMFiSAP_AESKeyLen ]; uint8_t aesMasterIV[ kMFiSAP_AESKeyLen ]; uint8_t * buf; uint8_t * dst; size_t len; if( ( UpTicks() - gMFiSAP_LastTicks ) < UpTicksPerSecond() ) { if( gMFiSAP_ThrottleCounter < 4 ) ++gMFiSAP_ThrottleCounter; SleepForUpTicks( gMFiSAP_ThrottleCounter * UpTicksPerSecond() ); } else { gMFiSAP_ThrottleCounter = 0; } gMFiSAP_LastTicks = UpTicks(); // Validate inputs. Input data must be: <1:version> <32:client's ECDH public key>. inputEnd = inInputPtr + inInputLen; require_action( inputEnd > inInputPtr, exit, err = kSizeErr ); // Detect bad length causing ptr wrap. require_action( ( inputEnd - inInputPtr ) >= kMFiSAP_VersionLen, exit, err = kSizeErr ); inRef->version = *inInputPtr++; require_action( inRef->version == kMFiSAPVersion1, exit, err = kVersionErr ); require_action( ( inputEnd - inInputPtr ) >= kMFiSAP_ECDHKeyLen, exit, err = kSizeErr ); clientECDHPublicKey = inInputPtr; inInputPtr += kMFiSAP_ECDHKeyLen; require_action( inInputPtr == inputEnd, exit, err = kSizeErr ); // Generate a random ECDH key pair. err = PlatformRandomBytes( ourPrivateKey, sizeof( ourPrivateKey ) ); require_noerr( err, exit ); curve25519_donna( ourPublicKey, ourPrivateKey, NULL ); // Use our private key and the client's public key to generate the shared secret. // Hash the shared secret and truncate it to form the AES master key. // Hash the shared secret with salt to derive the AES master IV. curve25519_donna( inRef->sharedSecret, ourPrivateKey, clientECDHPublicKey ); SHA1_Init( &sha1Context ); SHA1_Update( &sha1Context, kMFiSAP_AES_KEY_SaltPtr, kMFiSAP_AES_KEY_SaltLen ); SHA1_Update( &sha1Context, inRef->sharedSecret, sizeof( inRef->sharedSecret ) ); SHA1_Final( digest, &sha1Context ); memcpy( aesMasterKey, digest, sizeof( aesMasterKey ) ); SHA1_Init( &sha1Context ); SHA1_Update( &sha1Context, kMFiSAP_AES_IV_SaltPtr, kMFiSAP_AES_IV_SaltLen ); SHA1_Update( &sha1Context, inRef->sharedSecret, sizeof( inRef->sharedSecret ) ); SHA1_Final( digest, &sha1Context ); memcpy( aesMasterIV, digest, sizeof( aesMasterIV ) ); // Use the auth chip to sign a hash of <32:our ECDH public key> <32:client's ECDH public key>. // And copy the auth chip's certificate so the client can verify the signature. SHA1_Init( &sha1Context ); SHA1_Update( &sha1Context, ourPublicKey, sizeof( ourPublicKey ) ); SHA1_Update( &sha1Context, clientECDHPublicKey, kMFiSAP_ECDHKeyLen ); SHA1_Final( digest, &sha1Context ); err = PlatformMFiAuthCreateSignature( digest, sizeof( digest ), &signaturePtr, &signatureLen ); require_noerr( err, exit ); err = PlatformMFiAuthCopyCertificate( &certificatePtr, &certificateLen ); require_noerr( err, exit ); // Encrypt the signature with the AES master key and master IV. err = AES_CTR_Init( &inRef->aesMasterContext, aesMasterKey, aesMasterIV ); require_noerr( err, exit ); err = AES_CTR_Update( &inRef->aesMasterContext, signaturePtr, signatureLen, signaturePtr ); if( err ) AES_CTR_Final( &inRef->aesMasterContext ); require_noerr( err, exit ); inRef->aesMasterValid = true; // Return the response: // // <32:our ECDH public key> // <4:big endian certificate length> // <N:certificate data> // <4:big endian signature length> // <N:encrypted signature data> len = kMFiSAP_ECDHKeyLen + 4 + certificateLen + 4 + signatureLen; buf = (uint8_t *) malloc( len ); require_action( buf, exit, err = kNoMemoryErr ); dst = buf; memcpy( dst, ourPublicKey, sizeof( ourPublicKey ) ); dst += sizeof( ourPublicKey ); *dst++ = (uint8_t)( ( certificateLen >> 24 ) & 0xFF ); *dst++ = (uint8_t)( ( certificateLen >> 16 ) & 0xFF ); *dst++ = (uint8_t)( ( certificateLen >> 8 ) & 0xFF ); *dst++ = (uint8_t)( certificateLen & 0xFF ); memcpy( dst, certificatePtr, certificateLen ); dst += certificateLen; *dst++ = (uint8_t)( ( signatureLen >> 24 ) & 0xFF ); *dst++ = (uint8_t)( ( signatureLen >> 16 ) & 0xFF ); *dst++ = (uint8_t)( ( signatureLen >> 8 ) & 0xFF ); *dst++ = (uint8_t)( signatureLen & 0xFF ); memcpy( dst, signaturePtr, signatureLen ); dst += signatureLen; check( dst == ( buf + len ) ); *outOutputPtr = buf; *outOutputLen = (size_t)( dst - buf ); exit: if( certificatePtr ) free( certificatePtr ); if( signaturePtr ) free( signaturePtr ); return( err ); }