Skip to content

Commit 0dee01e

Browse files
Implement content encryption
This additionally allows a way to convert a WAD (struct) to a genuine WAD file.
1 parent c7d5578 commit 0dee01e

File tree

3 files changed

+108
-1
lines changed

3 files changed

+108
-1
lines changed

file.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,16 @@ func (w *WAD) LoadData(data []byte) error {
6262
return nil
6363
}
6464

65+
func (w *WAD) GetData() []byte {
66+
var data []byte
67+
for _, content := range w.Data {
68+
// Data internally is aligned by 64 bytes.
69+
data = append(data, pad(content.RawData)...)
70+
}
71+
72+
return data
73+
}
74+
6575
func (d *WADFile) DecryptData(titleKey [16]byte) error {
6676
content := d.ContentRecord
6777

@@ -101,3 +111,49 @@ func (d *WADFile) DecryptData(titleKey [16]byte) error {
101111
d.RawData = decryptedData
102112
return nil
103113
}
114+
115+
func (d *WADFile) EncryptData(titleKey [16]byte) error {
116+
content := d.ContentRecord
117+
118+
// The title's decrypted key will be what we'll encrypt with.
119+
block, err := aes.NewCipher(titleKey[:])
120+
if err != nil {
121+
return err
122+
}
123+
124+
// The IV we'll use will be the two bytes sourced from the content's index,
125+
// padded with 14 null bytes.
126+
var indexBytes [2]byte
127+
binary.BigEndian.PutUint16(indexBytes[:], content.Index)
128+
129+
iv := make([]byte, 16)
130+
iv[0] = indexBytes[0]
131+
iv[1] = indexBytes[1]
132+
133+
blockMode := cipher.NewCBCEncrypter(block, iv)
134+
135+
// One must encrypt content to 16 bytes.
136+
// We pad with null bytes.
137+
paddedSize := uint32(content.Size)
138+
leftover := paddedSize % 16
139+
if leftover != 0 {
140+
paddedSize += 16 - leftover
141+
}
142+
143+
decryptedData := make([]byte, paddedSize)
144+
copy(decryptedData, d.RawData)
145+
146+
// The resulting encrypted contents is the same size as our adjusted input, including padding.
147+
encryptedData := make([]byte, len(decryptedData))
148+
149+
// ...and we're off!
150+
blockMode.CryptBlocks(encryptedData, decryptedData)
151+
152+
// Update the content record to reflect the hash of our origin content.
153+
sha := sha1.Sum(d.RawData)
154+
d.Hash = sha
155+
156+
// We're all set!
157+
d.RawData = encryptedData
158+
return nil
159+
}

header.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ func (w *WAD) LoadHeader(source []byte) error {
3131

3232
func (w *WAD) GetHeader() ([]byte, error) {
3333
var tmp bytes.Buffer
34-
err := binary.Write(&tmp, binary.BigEndian, w)
34+
err := binary.Write(&tmp, binary.BigEndian, w.Header)
3535
if err != nil {
3636
panic(err)
3737
}

wad.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,14 @@ func getPadding(size uint32) uint32 {
3434
}
3535
}
3636

37+
func pad(src []byte) []byte {
38+
length := (uint32)(len(src))
39+
paddedSize := getPadding(length)
40+
resized := make([]byte, paddedSize+length)
41+
copy(resized, src)
42+
return resized
43+
}
44+
3745
// You use readable when you want to stagger contents and not use scary splice related methods.
3846
type readable struct {
3947
data []byte
@@ -118,3 +126,46 @@ func LoadWAD(contents []byte) (*WAD, error) {
118126

119127
return &wad, nil
120128
}
129+
130+
func (w *WAD) GetWAD(wadType WADType) ([]byte, error) {
131+
tmd, err := w.GetTMD()
132+
if err != nil {
133+
return nil, err
134+
}
135+
136+
ticket, err := w.GetTicket()
137+
if err != nil {
138+
return nil, err
139+
}
140+
141+
data := w.GetData()
142+
143+
// Create a header with our sourced content.
144+
header := WADHeader{
145+
// This size is fixed, and matches Nintendo's size.
146+
HeaderSize: 0x20,
147+
WADType: wadType,
148+
CertificateSize: (uint32)(len(w.CertificateChain)),
149+
CRLSize: (uint32)(len(w.CertificateRevocationList)),
150+
TicketSize: (uint32)(len(ticket)),
151+
TMDSize: (uint32)(len(tmd)),
152+
DataSize: (uint32)(len(data)),
153+
MetaSize: (uint32)(len(w.Meta)),
154+
}
155+
w.Header = header
156+
157+
headerContents, err := w.GetHeader()
158+
if err != nil {
159+
return nil, err
160+
}
161+
162+
var final []byte
163+
final = append(final, pad(headerContents)...)
164+
final = append(final, pad(w.CertificateChain)...)
165+
final = append(final, pad(w.CertificateRevocationList)...)
166+
final = append(final, pad(ticket)...)
167+
final = append(final, pad(tmd)...)
168+
final = append(final, pad(data)...)
169+
final = append(final, pad(w.Meta)...)
170+
return final, nil
171+
}

0 commit comments

Comments
 (0)