Skip to content

Commit b53587b

Browse files
committed
AES 256 GCM (in addition to CBC) see issue
#18 and existing PR #35 (credits to JP Bougie, I am just "porting" his code)
1 parent c1c349f commit b53587b

File tree

12 files changed

+85
-124
lines changed

12 files changed

+85
-124
lines changed

crypto/encrypt_test.go crypto/aes_cbc_test.go

+16-7
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ func TestSimpleEncrypt(t *testing.T) {
3737
var output bytes.Buffer
3838
var key [32]byte //not a safe key to have
3939

40-
err := Encrypt(key[:], input, &output)
40+
cbc := NewAESCBCEncrypter()
41+
42+
err := cbc.Encrypt(key[:], input, &output)
4143

4244
if err != nil {
4345
t.Log(err)
@@ -56,7 +58,9 @@ func TestConsecutiveEncrypts(t *testing.T) {
5658
var output bytes.Buffer
5759
var key [32]byte //not a safe key to have
5860

59-
err := Encrypt(key[:], input, &output)
61+
cbc := NewAESCBCEncrypter()
62+
63+
err := cbc.Encrypt(key[:], input, &output)
6064

6165
if err != nil {
6266
t.Log(err)
@@ -66,7 +70,7 @@ func TestConsecutiveEncrypts(t *testing.T) {
6670
input = bytes.NewBufferString("1234")
6771
var output2 bytes.Buffer
6872

69-
err = Encrypt(key[:], input, &output2)
73+
err = cbc.Encrypt(key[:], input, &output2)
7074

7175
if err != nil {
7276
t.Log(err)
@@ -81,7 +85,10 @@ func TestConsecutiveEncrypts(t *testing.T) {
8185
func TestFailingReaderForEncryption(t *testing.T) {
8286
var output bytes.Buffer
8387
var key [32]byte //not a safe key to have
84-
err := Encrypt(key[:], failingReader{}, &output)
88+
89+
cbc := NewAESCBCEncrypter()
90+
91+
err := cbc.Encrypt(key[:], failingReader{}, &output)
8592

8693
if err == nil {
8794
t.Error("expected an error from the reader")
@@ -93,12 +100,14 @@ func TestDecrypt(t *testing.T) {
93100
key := sha256.Sum256([]byte("password"))
94101
var cipher bytes.Buffer
95102

96-
err := Encrypt(key[:], clear, &cipher)
103+
cbc := &cbcEncrypter{}
104+
err := cbc.Encrypt(key[:], clear, &cipher)
97105
if err != nil {
98106
t.Fatal(err)
99107
}
108+
100109
var res bytes.Buffer
101-
err = Decrypt(key[:], &cipher, &res)
110+
err = cbc.Decrypt(key[:], &cipher, &res)
102111
if err != nil {
103112
t.Fatal(err)
104113
}
@@ -128,4 +137,4 @@ func TestKeyWrap(t *testing.T) {
128137
if !bytes.Equal(out, expected) {
129138
t.Errorf("Expected %x, got %x", expected, out)
130139
}
131-
}
140+
}

crypto/encrypt.go

+6-57
Original file line numberDiff line numberDiff line change
@@ -26,69 +26,18 @@
2626
package crypto
2727

2828
import (
29-
"bytes"
3029
"crypto/aes"
31-
"crypto/cipher"
32-
"crypto/rand"
3330
"io"
3431
)
3532

36-
func Encrypt(key []byte, r io.Reader, w io.Writer) error {
37-
r = PaddedReader(r, aes.BlockSize)
38-
39-
block, err := aes.NewCipher(key)
40-
if err != nil {
41-
return err
42-
}
43-
44-
// generate the IV
45-
iv := make([]byte, aes.BlockSize)
46-
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
47-
return err
48-
}
49-
50-
// write the IV first
51-
if _, err = w.Write(iv); err != nil {
52-
return err
53-
}
54-
55-
mode := cipher.NewCBCEncrypter(block, iv)
56-
buffer := make([]byte, aes.BlockSize)
57-
for _, err = io.ReadFull(r, buffer); err == nil; _, err = io.ReadFull(r, buffer) {
58-
mode.CryptBlocks(buffer, buffer)
59-
_, wErr := w.Write(buffer)
60-
if wErr != nil {
61-
return wErr
62-
}
63-
}
64-
65-
if err == nil || err == io.EOF {
66-
return nil
67-
}
68-
69-
return err
33+
type Encrypter interface {
34+
Encrypt(key ContentKey, r io.Reader, w io.Writer) error
35+
GenerateKey() (ContentKey, error)
36+
Signature() string
7037
}
7138

72-
func Decrypt(key []byte, r io.Reader, w io.Writer) error {
73-
block, err := aes.NewCipher(key)
74-
if err != nil {
75-
return err
76-
}
77-
78-
var buffer bytes.Buffer
79-
io.Copy(&buffer, r)
80-
81-
buf := buffer.Bytes()
82-
iv := buf[:aes.BlockSize]
83-
84-
mode := cipher.NewCBCDecrypter(block, iv)
85-
mode.CryptBlocks(buf[aes.BlockSize:], buf[aes.BlockSize:])
86-
87-
padding := buf[len(buf)-1]
88-
w.Write(buf[aes.BlockSize : len(buf)-int(padding)])
89-
90-
return nil
91-
39+
type Decrypter interface {
40+
Decrypt(key ContentKey, r io.Reader, w io.Writer) error
9241
}
9342

9443
var (

crypto/key.go

+3-5
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,10 @@ import (
2929
"crypto/rand"
3030
)
3131

32-
const (
33-
keyLength = 32 // 256 bits
34-
)
32+
type ContentKey []byte
3533

36-
func GenerateKey() ([]byte, error) {
37-
k := make([]byte, keyLength)
34+
func GenerateKey(size int) ([]byte, error) {
35+
k := make([]byte, size)
3836

3937
_, err := rand.Read(k)
4038
if err != nil {

crypto/key_test.go

+2-4
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,10 @@
2525

2626
package crypto
2727

28-
import (
29-
"testing"
30-
)
28+
import "testing"
3129

3230
func TestGenerateKey(t *testing.T) {
33-
buf, err := GenerateKey()
31+
buf, err := GenerateKey(aes256keyLength)
3432

3533
if err != nil {
3634
t.Error(err)

lcpencrypt/lcpencrypt.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import (
4242
"path/filepath"
4343
"strings"
4444

45+
"github.com/readium/readium-lcp-server/crypto"
4546
"github.com/readium/readium-lcp-server/epub"
4647
"github.com/readium/readium-lcp-server/lcpserver/api"
4748
"github.com/readium/readium-lcp-server/pack"
@@ -205,7 +206,8 @@ func main() {
205206
}
206207

207208
// pack / encrypt the epub content, fill the output file
208-
_, encryptionKey, err := pack.Do(ep, output)
209+
encrypter := crypto.NewAESCBCEncrypter()
210+
_, encryptionKey, err := pack.Do(encrypter, ep, output)
209211

210212
stats, err := output.Stat()
211213
if err == nil && (stats.Size() > 0) {

lcpserver/api/license.go

+17-13
Original file line numberDiff line numberDiff line change
@@ -127,11 +127,13 @@ func GetLicense(w http.ResponseWriter, r *http.Request, s Server) {
127127
problem.Error(w, r, problem.Problem{Detail: err.Error()}, http.StatusInternalServerError)
128128
return
129129
}
130+
131+
encrypter := s.Encrypter()
130132

131-
ExistingLicense.Encryption.ContentKey.Algorithm = "http://www.w3.org/2001/04/xmlenc#aes256-cbc"
132-
ExistingLicense.Encryption.ContentKey.Value = encryptKey(content.EncryptionKey, ExistingLicense.Encryption.UserKey.Value) //use old UserKey.Value
133+
ExistingLicense.Encryption.ContentKey.Algorithm = encrypter.Signature()
134+
ExistingLicense.Encryption.ContentKey.Value = encryptKey(encrypter, content.EncryptionKey, ExistingLicense.Encryption.UserKey.Value) //use old UserKey.Value
133135
ExistingLicense.Encryption.UserKey.Algorithm = "http://www.w3.org/2001/04/xmlenc#sha256"
134-
err = buildKeyCheck(&ExistingLicense, ExistingLicense.Encryption.UserKey.Value)
136+
err = buildKeyCheck(encrypter, &ExistingLicense, ExistingLicense.Encryption.UserKey.Value)
135137
if err != nil {
136138
problem.Error(w, r, problem.Problem{Detail: err.Error()}, http.StatusBadRequest)
137139
return
@@ -410,6 +412,8 @@ func completeLicense(l *license.License, key string, s Server) error {
410412
return err
411413
}
412414

415+
encrypter := s.Encrypter()
416+
413417
license.Prepare(l)
414418
l.ContentId = key
415419
links := new([]license.Link)
@@ -450,15 +454,15 @@ func completeLicense(l *license.License, key string, s Server) error {
450454
encryptionKey = hash[:]
451455
}
452456

453-
l.Encryption.ContentKey.Algorithm = "http://www.w3.org/2001/04/xmlenc#aes256-cbc"
454-
l.Encryption.ContentKey.Value = encryptKey(c.EncryptionKey, encryptionKey[:])
457+
l.Encryption.ContentKey.Algorithm = encrypter.Signature()
458+
l.Encryption.ContentKey.Value = encryptKey(encrypter, c.EncryptionKey, encryptionKey[:])
455459
l.Encryption.UserKey.Algorithm = "http://www.w3.org/2001/04/xmlenc#sha256"
456460

457-
err = encryptFields(l, encryptionKey[:])
461+
err = encryptFields(encrypter, l, encryptionKey[:])
458462
if err != nil {
459463
return err
460464
}
461-
err = buildKeyCheck(l, encryptionKey[:])
465+
err = buildKeyCheck(encrypter, l, encryptionKey[:])
462466
if err != nil {
463467
return err
464468
}
@@ -474,21 +478,21 @@ func completeLicense(l *license.License, key string, s Server) error {
474478
return nil
475479
}
476480

477-
func buildKeyCheck(l *license.License, key []byte) error {
481+
func buildKeyCheck(encrypter crypto.Encrypter, l *license.License, key []byte) error {
478482
var out bytes.Buffer
479-
err := crypto.Encrypt(key, bytes.NewBufferString(l.Id), &out)
483+
err := encrypter.Encrypt(key, bytes.NewBufferString(l.Id), &out)
480484
if err != nil {
481485
return err
482486
}
483487
l.Encryption.UserKey.Check = out.Bytes()
484488
return nil
485489
}
486490

487-
func encryptFields(l *license.License, key []byte) error {
491+
func encryptFields(encrypter crypto.Encrypter, l *license.License, key []byte) error {
488492
for _, toEncrypt := range l.User.Encrypted {
489493
var out bytes.Buffer
490494
field := getField(&l.User, toEncrypt)
491-
err := crypto.Encrypt(key[:], bytes.NewBufferString(field.String()), &out)
495+
err := encrypter.Encrypt(key[:], bytes.NewBufferString(field.String()), &out)
492496
if err != nil {
493497
return err
494498
}
@@ -516,10 +520,10 @@ func signLicense(l *license.License, cert *tls.Certificate) error {
516520
return nil
517521
}
518522

519-
func encryptKey(key []byte, kek []byte) []byte {
523+
func encryptKey(encrypter crypto.Encrypter, key []byte, kek []byte) []byte {
520524
var out bytes.Buffer
521525
in := bytes.NewReader(key)
522-
crypto.Encrypt(kek[:], in, &out)
526+
encrypter.Encrypt(kek[:], in, &out)
523527
return out.Bytes()
524528
}
525529

lcpserver/api/store.go

+3
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ import (
3535
"os"
3636

3737
"github.com/gorilla/mux"
38+
39+
"github.com/readium/readium-lcp-server/crypto"
3840
"github.com/readium/readium-lcp-server/api"
3941
"github.com/readium/readium-lcp-server/epub"
4042
"github.com/readium/readium-lcp-server/index"
@@ -50,6 +52,7 @@ type Server interface {
5052
Licenses() license.Store
5153
Certificate() *tls.Certificate
5254
Source() *pack.ManualSource
55+
Encrypter() crypto.Encrypter
5356
}
5457

5558
// struct for communication with lcp-server

lcpserver/server/server.go

+5
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import (
3333
"github.com/abbot/go-http-auth"
3434
"github.com/gorilla/mux"
3535

36+
"github.com/readium/readium-lcp-server/crypto"
3637
"github.com/readium/readium-lcp-server/api"
3738
"github.com/readium/readium-lcp-server/index"
3839
"github.com/readium/readium-lcp-server/lcpserver/api"
@@ -71,6 +72,10 @@ func (s *Server) Source() *pack.ManualSource {
7172
return &s.source
7273
}
7374

75+
func (s *Server) Encrypter() crypto.Encrypter {
76+
return crypto.NewAESCBCEncrypter()
77+
}
78+
7479
func New(bindAddr string, tplPath string, readonly bool, idx *index.Index, st *storage.Store, lst *license.Store, cert *tls.Certificate, packager *pack.Packager, basicAuth *auth.BasicAuth) *Server {
7580

7681
sr := api.CreateServerRouter(tplPath)

license/store_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ func TestStoreAdd(t *testing.T) {
6666
Prepare(&l)
6767
err = st.Add(l)
6868
if err != nil {
69-
t.Error(err)
69+
t.Fatal(err)
7070
}
7171

7272
l2, err := st.Get(l.Id)

pack/pack.go

+6-20
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ import (
3737
"github.com/readium/readium-lcp-server/xmlenc"
3838
)
3939

40-
func Do(ep epub.Epub, w io.Writer) (enc *xmlenc.Manifest, key []byte, err error) {
41-
key, err = crypto.GenerateKey()
40+
func Do(encrypter crypto.Encrypter, ep epub.Epub, w io.Writer) (enc *xmlenc.Manifest, key crypto.ContentKey, err error) {
41+
key, err = encrypter.GenerateKey()
4242
if err != nil {
4343
return
4444
}
@@ -52,7 +52,7 @@ func Do(ep epub.Epub, w io.Writer) (enc *xmlenc.Manifest, key []byte, err error)
5252
for _, res := range ep.Resource {
5353
if _, alreadyEncrypted := ep.Encryption.DataForFile(res.Path); !alreadyEncrypted && canEncrypt(res, ep) {
5454
toCompress := mustCompressBeforeEncryption(*res, ep)
55-
err = encryptFile(key, ep.Encryption, res, toCompress, ew)
55+
err = encryptFile(encrypter, key, ep.Encryption, res, toCompress, ew)
5656
if err != nil {
5757
return
5858
}
@@ -90,9 +90,9 @@ func canEncrypt(file *epub.Resource, ep epub.Epub) bool {
9090
return ep.CanEncrypt(file.Path)
9191
}
9292

93-
func encryptFile(key []byte, m *xmlenc.Manifest, file *epub.Resource, compress bool, w *epub.Writer) error {
93+
func encryptFile(encrypter crypto.Encrypter, key []byte, m *xmlenc.Manifest, file *epub.Resource, compress bool, w *epub.Writer) error {
9494
data := xmlenc.Data{}
95-
data.Method.Algorithm = "http://www.w3.org/2001/04/xmlenc#aes256-cbc"
95+
data.Method.Algorithm = xmlenc.URI(encrypter.Signature())
9696
data.KeyInfo = &xmlenc.KeyInfo{}
9797
data.KeyInfo.RetrievalMethod.URI = "license.lcpl#/encryption/content_key"
9898
data.KeyInfo.RetrievalMethod.Type = "http://readium.org/2014/01/lcp#EncryptedContentKey"
@@ -133,21 +133,7 @@ func encryptFile(key []byte, m *xmlenc.Manifest, file *epub.Resource, compress b
133133
if err != nil {
134134
return err
135135
}
136-
return crypto.Encrypt(key, input, fw)
137-
}
138-
139-
func Undo(key []byte, ep epub.Epub) (epub.Epub, error) {
140-
for _, data := range ep.Encryption.Data {
141-
if res, ok := findFile(string(data.CipherData.CipherReference.URI), ep); ok {
142-
var buf bytes.Buffer
143-
crypto.Decrypt(key, res.Contents, &buf)
144-
res.Contents = &buf
145-
}
146-
}
147-
148-
ep.Encryption = nil
149-
150-
return ep, nil
136+
return encrypter.Encrypt(key, input, fw)
151137
}
152138

153139
func findFile(name string, ep epub.Epub) (*epub.Resource, bool) {

0 commit comments

Comments
 (0)