Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Obfuscated fonts #16

Merged
merged 3 commits into from
Feb 11, 2017
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "test/readium-test-files"]
path = test/readium-test-files
url = [email protected]:readium/readium-test-files.git
67 changes: 67 additions & 0 deletions decoder/adobe_fonts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package decoder

import (
"bytes"
"errors"
"io"
"io/ioutil"
"strconv"
"strings"
"unicode"

"github.com/feedbooks/r2-streamer-go/models"
)

func init() {
decoderList = append(decoderList, List{decoderAlgorithm: "http://ns.adobe.com/pdf/enc#RC", decoder: DecodeAdobeFont})
}

// DecodeAdobeFont decode obfuscate fonts using idpf spec http://www.idpf.org/epub/20/spec/FontManglingSpec.html
func DecodeAdobeFont(publication models.Publication, link models.Link, reader io.ReadSeeker) (io.ReadSeeker, error) {
var count int

key := getAdobeHashKey(publication)
if string(key) == "" {
return nil, errors.New("can't find hash key")
}

buff, _ := ioutil.ReadAll(reader)
if len(buff) > 1024 {
count = 1024
} else {
count = len(buff)
}

j := 0
for i := 0; i < count; i++ {
buff[i] = buff[i] ^ key[j]

j++
if j == 16 {
j = 0
}
}
readerSeeker := bytes.NewReader(buff)
return readerSeeker, nil
}

func getAdobeHashKey(publication models.Publication) []byte {
var stringKey []rune
var key []byte

id := strings.Replace(publication.Metadata.Identifier, "urn:uuid:", "", -1)
id = strings.Replace(id, "-", "", -1)
for _, c := range id {
if !unicode.IsSpace(c) {
stringKey = append(stringKey, c)
}
}

for i := 0; i < 16; i++ {
byteHex := stringKey[i*2 : i*2+2]
byteNumer, _ := strconv.ParseInt(string(byteHex), 16, 32)
key = append(key, byte(byteNumer))
}

return key
}
30 changes: 30 additions & 0 deletions decoder/decoder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package decoder

import (
"errors"
"fmt"
"io"

"github.com/feedbooks/r2-streamer-go/models"
)

// List TODO add doc
type List struct {
decoderAlgorithm string
decoder (func(models.Publication, models.Link, io.ReadSeeker) (io.ReadSeeker, error))
}

var decoderList []List

// Decode decode the ressource
func Decode(publication models.Publication, link models.Link, reader io.ReadSeeker) (io.ReadSeeker, error) {

fmt.Println(link.CryptAlgorithm)
for _, decoderFunc := range decoderList {
if link.CryptAlgorithm == decoderFunc.decoderAlgorithm {
return decoderFunc.decoder(publication, link, reader)
}
}

return nil, errors.New("can't find fetcher")
}
52 changes: 52 additions & 0 deletions decoder/fonts_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package decoder

import (
"bytes"
"io/ioutil"
"os"
"testing"

"github.com/feedbooks/r2-streamer-go/models"
"github.com/feedbooks/r2-streamer-go/parser"
. "github.com/smartystreets/goconvey/convey"
)

var testPublication models.Publication
var testFonts []byte

func init() {

testPublication, _ = parser.Parse("../test/readium-test-files/functional/smoke-tests/SmokeTestFXL")
ft, _ := os.Open("../test/readium-test-files/functional/smoke-tests/SmokeTestFXL/fonts/cut-cut.woff")
testFonts, _ = ioutil.ReadAll(ft)
}

func TestAdobeFonts(t *testing.T) {

f, _ := os.Open("../test/readium-test-files/functional/smoke-tests/SmokeTestFXL/fonts/cut-cut.adb.woff")

Convey("Given cut-cut.adb.woff fonts", t, func() {
fd, _ := DecodeAdobeFont(testPublication, models.Link{}, f)
buff, _ := ioutil.ReadAll(fd)
Convey("The adobe fonts is deobfuscated", func() {
So(bytes.Equal(buff, testFonts), ShouldBeTrue)
})

})

}

func TestIdpfFonts(t *testing.T) {

f, _ := os.Open("../test/readium-test-files/functional/smoke-tests/SmokeTestFXL/fonts/cut-cut.obf.woff")

Convey("Given cut-cut.obf.woff fonts", t, func() {
fd, _ := DecodeIdpfFont(testPublication, models.Link{}, f)
buff, _ := ioutil.ReadAll(fd)
Convey("The idpf fonts is deobfuscated", func() {
So(bytes.Equal(buff, testFonts), ShouldBeTrue)
})

})

}
62 changes: 62 additions & 0 deletions decoder/idpf_fonts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package decoder

import (
"bytes"
"crypto/sha1"
"errors"
"fmt"
"io"
"io/ioutil"
"unicode"

"github.com/feedbooks/r2-streamer-go/models"
)

func init() {
decoderList = append(decoderList, List{decoderAlgorithm: "http://www.idpf.org/2008/embedding", decoder: DecodeIdpfFont})
}

// DecodeIdpfFont decode obfuscate fonts using idpf spec http://www.idpf.org/epub/20/spec/FontManglingSpec.html
func DecodeIdpfFont(publication models.Publication, link models.Link, reader io.ReadSeeker) (io.ReadSeeker, error) {
var count int

key := getHashKey(publication)
fmt.Println(key)
if string(key) == "" {
return nil, errors.New("can't find hash key")
}

buff, _ := ioutil.ReadAll(reader)
if len(buff) > 1040 {
count = 1040
} else {
count = len(buff)
}

j := 0
for i := 0; i < count; i++ {
buff[i] = buff[i] ^ key[j]

j++
if j == 20 {
j = 0
}
}
readerSeeker := bytes.NewReader(buff)
return readerSeeker, nil
}

func getHashKey(publication models.Publication) []byte {
var stringKey []rune

for _, c := range publication.Metadata.Identifier {
if !unicode.IsSpace(c) {
stringKey = append(stringKey, c)
}
}

h := sha1.New()
io.WriteString(h, string(stringKey))

return h.Sum(nil)
}
11 changes: 11 additions & 0 deletions fetcher/epub.go
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@ import (
"io"
"io/ioutil"

"github.com/feedbooks/r2-streamer-go/decoder"
"github.com/feedbooks/r2-streamer-go/models"
)

@@ -18,6 +19,7 @@ func FetchEpub(publication models.Publication, publicationResource string) (io.R
var mediaType string
var reader *zip.ReadCloser
var assetFd io.ReadCloser
var link models.Link

for _, data := range publication.Internal {
if data.Name == "epub" {
@@ -32,9 +34,18 @@ func FetchEpub(publication models.Publication, publicationResource string) (io.R
}
}

for _, linkRes := range publication.Resources {
if publicationResource == linkRes.Href {
link = linkRes
}
}
buff, _ := ioutil.ReadAll(assetFd)
assetFd.Close()
readerSeeker := bytes.NewReader(buff)
readerSeekerDecode, err := decoder.Decode(publication, link, readerSeeker)
if err != nil {
return readerSeekerDecode, mediaType, nil
}

return readerSeeker, mediaType, nil
}
12 changes: 12 additions & 0 deletions fetcher/epub_dir.go
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@ import (
"os"
"path"

"github.com/feedbooks/r2-streamer-go/decoder"
"github.com/feedbooks/r2-streamer-go/models"
)

@@ -18,6 +19,7 @@ func FetchEpubDir(publication models.Publication, publicationResource string) (i
var mediaType string
var basePath string
var rootFile string
var link models.Link

for _, data := range publication.Internal {
if data.Name == "basepath" {
@@ -34,5 +36,15 @@ func FetchEpubDir(publication models.Publication, publicationResource string) (i
fmt.Println(err)
}

for _, linkRes := range publication.Resources {
if publicationResource == linkRes.Href {
link = linkRes
}
}

readerSeekerDecode, err := decoder.Decode(publication, link, fd)
if err == nil {
return readerSeekerDecode, mediaType, nil
}
return fd, mediaType, nil
}
21 changes: 11 additions & 10 deletions models/publication.go
Original file line number Diff line number Diff line change
@@ -31,16 +31,17 @@ type Internal struct {

// Link object used in collections and links
type Link struct {
Href string `json:"href"`
TypeLink string `json:"type,omitempty"`
Rel []string `json:"rel,omitempty"`
Height int `json:"height,omitempty"`
Width int `json:"width,omitempty"`
Title string `json:"title,omitempty"`
Properties *Properties `json:"properties,omitempty"`
Duration string `json:"duration,omitempty"`
Templated bool `json:"templated,omitempty"`
Children []Link `json:"children,omitempty"`
Href string `json:"href"`
TypeLink string `json:"type,omitempty"`
Rel []string `json:"rel,omitempty"`
Height int `json:"height,omitempty"`
Width int `json:"width,omitempty"`
Title string `json:"title,omitempty"`
Properties *Properties `json:"properties,omitempty"`
Duration string `json:"duration,omitempty"`
Templated bool `json:"templated,omitempty"`
Children []Link `json:"children,omitempty"`
CryptAlgorithm string `json:"-"`
}

// PublicationCollection is used as an extension points for other collections in a Publication
42 changes: 39 additions & 3 deletions parser/epub.go
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@ package parser

import (
"errors"
"path"
"path/filepath"
"strconv"
"strings"
@@ -34,16 +35,19 @@ func EpubParser(filePath string) (models.Publication, error) {
fileExt := filepath.Ext(filePath)
if fileExt == "" {
book, err = epub.OpenDir(filePath)
if err != nil {
return models.Publication{}, errors.New("can't open or parse epub file with err : " + err.Error())
}
publication.Internal = append(publication.Internal, models.Internal{Name: "type", Value: "epub_dir"})
publication.Internal = append(publication.Internal, models.Internal{Name: "basepath", Value: filePath})
} else {
book, err = epub.Open(filePath)
if err != nil {
return models.Publication{}, errors.New("can't open or parse epub file with err : " + err.Error())
}
publication.Internal = append(publication.Internal, models.Internal{Name: "type", Value: "epub"})
publication.Internal = append(publication.Internal, models.Internal{Name: "epub", Value: book.ZipReader()})
}
if err != nil {
return models.Publication{}, errors.New("can't open or parse epub file with err : " + err.Error())
}

if book.Container.Rootfile.Version != "" {
epubVersion = book.Container.Rootfile.Version
@@ -106,6 +110,8 @@ func EpubParser(filePath string) (models.Publication, error) {
}

fillCalibreSerieInfo(&publication, book)
fillEncryptionInfo(&publication, book)

return publication, nil
}

@@ -558,3 +564,33 @@ func fillCalibreSerieInfo(publication *models.Publication, book *epub.Book) {
}

}

func fillEncryptionInfo(publication *models.Publication, book *epub.Book) {

for _, encInfo := range book.Encryption.EncryptedData {
resURI := encInfo.CipherData.CipherReference.URI
for i, l := range publication.Resources {
if resURI == FilePath(*publication, l.Href) {
publication.Resources[i].CryptAlgorithm = encInfo.EncryptionMethod.Algorithm
}
}
for i, l := range publication.Spine {
if resURI == FilePath(*publication, l.Href) {
publication.Spine[i].CryptAlgorithm = encInfo.EncryptionMethod.Algorithm
}
}
}
}

// FilePath return the complete path for the ressource
func FilePath(publication models.Publication, publicationResource string) string {
var rootFile string

for _, data := range publication.Internal {
if data.Name == "rootfile" {
rootFile = data.Value.(string)
}
}

return path.Join(path.Dir(rootFile), publicationResource)
}
1 change: 1 addition & 0 deletions test/readium-test-files
Submodule readium-test-files added at 321967