//TODO: The fwrite/fread error checking was broken. //Maybe the MS runtime is returning the number of bytes written instead of the element count? BOOL __stdcall np_sign_file(s8 *fname) { u8 padding_data[0x10] = { 0xbc, 0x3f, 0x7a, 0x48, 0xaf, 0x45, 0xef, 0x28, 0x3a, 0x05, 0x98, 0x10, 0xbc, 0x3f, 0x7a, 0x48 }; keyset_t *ks; FILE *fp = NULL; u8 *buffer = NULL; u32 length; u32 padding; u8 hash[0x14], R[0x15], S[0x15]; //Try to find keyset. if((ks = keyset_find_by_name(CONFIG_NP_SIG_KNAME)) == NULL) return FALSE; if((fp = fopen(fname, "r+b")) == NULL) return FALSE; fseek(fp, 0, SEEK_END); length = ftell(fp); padding = length % 0x10; if(padding > 0) { fwrite(padding_data, sizeof(u8), padding, fp); length += padding; } fseek(fp, 0, SEEK_SET); if((buffer = (u8 *)calloc(length, sizeof(char))) == NULL) { fclose(fp); return FALSE; } fread(buffer, sizeof(u8), length, fp); //Generate header hash. sha1(buffer, length, hash); //Generate signature. /* TODO: Set the right curve and private key */ ecdsa_set_curve(ks->ctype | USE_VSH_CURVE); ecdsa_set_pub(ks->pub); ecdsa_set_priv(ks->priv); ecdsa_sign(hash, R, S); fseek(fp, 0, SEEK_END); fwrite(R + 1, 0x14, 1, fp); fwrite(S + 1, 0x14, 1, fp); /* Let's be as stupid as sony here... */ fwrite(hash + 0xC, 8, 1, fp); free(buffer); fclose(fp); return TRUE; }
int add_npdrm_footer_sig(const char *filename) { FILE *fp; uint8_t s[21]; uint8_t r[21]; uint8_t hash[20]; static char padding[] = { 0x8b, 0x3f, 0x7a,0x48, 0xaf, 0x45, 0xef, 0x28, 0x3a, 0x05, 0x98, 0x10, 0xbc, 0x3f, 0x7a, 0x48 }; keyset_t *keyset = find_keyset_by_name("NP_sig"); if ( !keyset) return 0; fp = fopen(filename, "r+b"); if (!fp) return 0; fseek(fp, 0, SEEK_END); size_t size = ftell (fp); // Error ? SCETool takes left_not_aligned as (size & 0xF) size_t left_not_aligned = (0x10 - (size & 0xF)) & 0x0F; if (left_not_aligned) { fwrite(padding, 1, left_not_aligned, fp); size += left_not_aligned; } fseek(fp, 0, SEEK_SET); uint8_t *buffer = malloc(size); if (!buffer) { fclose(fp); return 0; } if(fread(buffer, 1, size, fp) != size) return 0; sha1(buffer, size, hash); ecdsa_set_curve(keyset->ctype | 0x40); ecdsa_set_pub(keyset->pub_key); ecdsa_set_priv(keyset->priv_key); ecdsa_sign(r, s, hash); fseek(fp, 0, SEEK_END); fwrite(&r[1], 20, 1, fp); fwrite(&s[1], 20, 1, fp); fwrite(&hash[12], 8, 1, fp); free(buffer); fclose(fp); return 1; }
static void get_keys(const char *suffix) { if (key_get(type, suffix, &ks) < 0) fail("key_get failed"); if (ks.pub_avail < 0) fail("no public key available"); if (ks.priv_avail < 0) fail("no private key available"); if (ecdsa_set_curve(ks.ctype) < 0) fail("ecdsa_set_curve failed"); ecdsa_set_pub(ks.pub); ecdsa_set_priv(ks.priv); }
int edata_check_ecdsa(u8 *edata_buf) { u8 sha1_hash[20]; int retv; printf("EDATA ID: %s\n", (char*)(edata_buf+0x10)); ecdsa_set_curve(&ecdsa_app); ecdsa_set_pub(pubkey_edat_x, pubkey_edat_y); SHA1(edata_buf, 0x58, sha1_hash); retv = ecdsa_verify(sha1_hash, edata_buf+0x58, edata_buf+0x6c); if(retv==0){ //printf("ECDSA verify passed!\n"); }else{ printf("edata_check_ecdsa: ECDSA verify failed!\n"); } return retv; }
static void decrypt(u8* ptr) { if (keyid < 0) keyid = sce_decrypt_header(ptr, klist); else if (keyid != sce_decrypt_header(ptr, klist)) fail("Both files must have the same key id"); if (keyid < 0) fail("sce_decrypt_header failed"); if (sce_decrypt_data(ptr) < 0) fail("sce_decrypt_data failed"); if (klist->keys[keyid].pub_avail < 0) fail("no public key available"); if (ecdsa_set_curve(klist->keys[keyid].ctype) < 0) fail("ecdsa_set_curve failed"); ecdsa_set_pub(klist->keys[keyid].pub); }
static void decrypt(void) { int keyid; sce_remove_npdrm(ptr, klist); keyid = sce_decrypt_header(ptr, klist); if (keyid < 0) fail("sce_decrypt_header failed"); if (sce_decrypt_data(ptr) < 0) fail("sce_decrypt_data failed"); if (klist->keys[keyid].pub_avail < 0) fail("no public key available"); if (ecdsa_set_curve(klist->keys[keyid].ctype) < 0) fail("ecdsa_set_curve failed"); ecdsa_set_pub(klist->keys[keyid].pub); }
// set file offset to beginning before calling int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs::file* f, bool verbose) { u8 header[0xA0] = { 0 }; u8 empty_header[0xA0] = { 0 }; u8 header_hash[0x10] = { 0 }; u8 metadata_hash[0x10] = { 0 }; const u64 file_offset = f->pos(); // Check NPD version and flags. if ((npd->version == 0) || (npd->version == 1)) { if (edat->flags & 0x7EFFFFFE) { LOG_ERROR(LOADER, "EDAT: Bad header flags!"); return 1; } } else if (npd->version == 2) { if (edat->flags & 0x7EFFFFE0) { LOG_ERROR(LOADER, "EDAT: Bad header flags!"); return 1; } } else if ((npd->version == 3) || (npd->version == 4)) { if (edat->flags & 0x7EFFFFC0) { LOG_ERROR(LOADER, "EDAT: Bad header flags!"); return 1; } } else { LOG_ERROR(LOADER, "EDAT: Unknown version!"); return 1; } // Read in the file header. f->read(header, 0xA0); // Read in the header and metadata section hashes. f->seek(file_offset + 0x90); f->read(metadata_hash, 0x10); f->read(header_hash, 0x10); // Setup the hashing mode and the crypto mode used in the file. const int crypto_mode = 0x1; int hash_mode = ((edat->flags & EDAT_ENCRYPTED_KEY_FLAG) == 0) ? 0x00000002 : 0x10000002; if ((edat->flags & EDAT_DEBUG_DATA_FLAG) != 0) { hash_mode |= 0x01000000; if (verbose) LOG_WARNING(LOADER, "EDAT: DEBUG data detected!"); } // Setup header key and iv buffers. unsigned char header_key[0x10] = { 0 }; unsigned char header_iv[0x10] = { 0 }; // Test the header hash (located at offset 0xA0). if (!decrypt(hash_mode, crypto_mode, (npd->version == 4), header, empty_header, 0xA0, header_key, header_iv, key, header_hash)) { if (verbose) LOG_WARNING(LOADER, "EDAT: Header hash is invalid!"); // If the header hash test fails and the data is not DEBUG, then RAP/RIF/KLIC key is invalid. if ((edat->flags & EDAT_DEBUG_DATA_FLAG) != EDAT_DEBUG_DATA_FLAG) { LOG_ERROR(LOADER, "EDAT: RAP/RIF/KLIC key is invalid!"); return 1; } } // Parse the metadata info. const int metadata_section_size = ((edat->flags & EDAT_COMPRESSED_FLAG) != 0 || (edat->flags & EDAT_FLAG_0x20) != 0) ? 0x20 : 0x10; if (((edat->flags & EDAT_COMPRESSED_FLAG) != 0)) { if (verbose) LOG_WARNING(LOADER, "EDAT: COMPRESSED data detected!"); } const int block_num = (int)((edat->file_size + edat->block_size - 1) / edat->block_size); const int metadata_offset = 0x100; const int metadata_size = metadata_section_size * block_num; u64 metadata_section_offset = metadata_offset; long bytes_read = 0; long bytes_to_read = metadata_size; std::unique_ptr<u8> metadata(new u8[metadata_size]); std::unique_ptr<u8> empty_metadata(new u8[metadata_size]); while (bytes_to_read > 0) { // Locate the metadata blocks. f->seek(file_offset + metadata_section_offset); // Read in the metadata. f->read(metadata.get() + bytes_read, metadata_section_size); // Adjust sizes. bytes_read += metadata_section_size; bytes_to_read -= metadata_section_size; if (((edat->flags & EDAT_FLAG_0x20) != 0)) // Metadata block before each data block. metadata_section_offset += (metadata_section_size + edat->block_size); else metadata_section_offset += metadata_section_size; } // Test the metadata section hash (located at offset 0x90). if (!decrypt(hash_mode, crypto_mode, (npd->version == 4), metadata.get(), empty_metadata.get(), metadata_size, header_key, header_iv, key, metadata_hash)) { if (verbose) LOG_WARNING(LOADER, "EDAT: Metadata section hash is invalid!"); } // Checking ECDSA signatures. if ((edat->flags & EDAT_DEBUG_DATA_FLAG) == 0) { // Setup buffers. unsigned char metadata_signature[0x28] = { 0 }; unsigned char header_signature[0x28] = { 0 }; unsigned char signature_hash[20] = { 0 }; unsigned char signature_r[0x15] = { 0 }; unsigned char signature_s[0x15] = { 0 }; unsigned char zero_buf[0x15] = { 0 }; // Setup ECDSA curve and public key. ecdsa_set_curve(VSH_CURVE_P, VSH_CURVE_A, VSH_CURVE_B, VSH_CURVE_N, VSH_CURVE_GX, VSH_CURVE_GY); ecdsa_set_pub(VSH_PUB); // Read in the metadata and header signatures. f->seek(file_offset + 0xB0); f->read(metadata_signature, 0x28); f->read(header_signature, 0x28); // Checking metadata signature. // Setup signature r and s. memcpy(signature_r + 01, metadata_signature, 0x14); memcpy(signature_s + 01, metadata_signature + 0x14, 0x14); if ((!memcmp(signature_r, zero_buf, 0x15)) || (!memcmp(signature_s, zero_buf, 0x15))) LOG_WARNING(LOADER, "EDAT: Metadata signature is invalid!"); else { // Setup signature hash. if ((edat->flags & EDAT_FLAG_0x20) != 0) //Sony failed again, they used buffer from 0x100 with half size of real metadata. { int metadata_buf_size = block_num * 0x10; std::unique_ptr<u8> metadata_buf(new u8[metadata_buf_size]); f->seek(file_offset + metadata_offset); f->read(metadata_buf.get(), metadata_buf_size); sha1(metadata_buf.get(), metadata_buf_size, signature_hash); } else sha1(metadata.get(), metadata_size, signature_hash); if (!ecdsa_verify(signature_hash, signature_r, signature_s)) { LOG_WARNING(LOADER, "EDAT: Metadata signature is invalid!"); if (((unsigned long long)edat->block_size * block_num) > 0x100000000) LOG_WARNING(LOADER, "EDAT: *Due to large file size, metadata signature status may be incorrect!"); } } // Checking header signature. // Setup header signature r and s. memset(signature_r, 0, 0x15); memset(signature_s, 0, 0x15); memcpy(signature_r + 01, header_signature, 0x14); memcpy(signature_s + 01, header_signature + 0x14, 0x14); if ((!memcmp(signature_r, zero_buf, 0x15)) || (!memcmp(signature_s, zero_buf, 0x15))) LOG_WARNING(LOADER, "EDAT: Header signature is invalid!"); else { // Setup header signature hash. memset(signature_hash, 0, 20); std::unique_ptr<u8> header_buf(new u8[0xD8]); f->seek(file_offset); f->read(header_buf.get(), 0xD8); sha1(header_buf.get(), 0xD8, signature_hash ); if (!ecdsa_verify(signature_hash, signature_r, signature_s)) LOG_WARNING(LOADER, "EDAT: Header signature is invalid!"); } } return 0; }