-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
For various reasons, this does not properly work when contents sizes are not multiples of 16 (read: the AES block size in use).
- Loading branch information
1 parent
4f9e3d0
commit d3970c3
Showing
2 changed files
with
72 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,73 @@ | ||
package wadlib | ||
|
||
import "crypto/sha1" | ||
|
||
var sha = sha1.New() | ||
import ( | ||
"bytes" | ||
"crypto/aes" | ||
"crypto/cipher" | ||
"crypto/sha1" | ||
"encoding/binary" | ||
"errors" | ||
"fmt" | ||
) | ||
|
||
type WADFile struct { | ||
ContentRecord | ||
RawData []byte | ||
} | ||
|
||
func readData(data []byte, contents []ContentRecord, titleKey [16]byte) ([]WADFile, error) { | ||
// Each content within the data section is aligned to a 0x40/64-byte boundary. | ||
r := readable{ | ||
data: data, | ||
} | ||
|
||
// TODO: We naively assume that the index will be accurately indexed from 0. | ||
// All observed Nintendo files follow this, but external tools may not follow this format. | ||
// We should most likely apply max index validation and sort, | ||
// otherwise data will read out of order in these cases. | ||
|
||
// All data contents will be the same amount as the number of contents per TMD. | ||
wads := make([]WADFile, len(contents)) | ||
for _, content := range contents { | ||
// It's okay to cast this from a uint64 as the WAD file format | ||
// cannot exceed the maximum uint32 value within the data section. | ||
encryptedData := r.getRange(uint32(content.Size)) | ||
|
||
// The title's decrypted key will be what we'll decrypt with. | ||
block, err := aes.NewCipher(titleKey[:]) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// The IV we'll use will be the two bytes sourced from the content's index, | ||
// padded with 14 null bytes. | ||
var indexBytes [2]byte | ||
binary.BigEndian.PutUint16(indexBytes[:], content.Index) | ||
|
||
iv := make([]byte, 16) | ||
iv[0] = indexBytes[0] | ||
iv[1] = indexBytes[1] | ||
|
||
blockMode := cipher.NewCBCDecrypter(block, iv) | ||
|
||
// The resulting decrypted contents is the same size as the input, including padding. | ||
decryptedData := make([]byte, content.Size) | ||
|
||
// ...and we're off! | ||
blockMode.CryptBlocks(decryptedData, encryptedData) | ||
|
||
// Ensure that the decrypted data matches the SHA-1 hash given in the contents list. | ||
sha := sha1.Sum(decryptedData) | ||
if bytes.Compare(sha[:], content.Hash[:]) != 0 { | ||
return nil, errors.New(fmt.Sprintf("content %08x did not match the noted hash when decrypted", content.ID)) | ||
} | ||
|
||
// We're all set! | ||
wads[content.Index] = WADFile{ | ||
content, | ||
decryptedData, | ||
} | ||
} | ||
|
||
return wads, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters