Skip to content

Commit 03162f5

Browse files
committed
Initial commit.
0 parents  commit 03162f5

File tree

3 files changed

+225
-0
lines changed

3 files changed

+225
-0
lines changed

LICENSE

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
Copyright (c) 2013 Coding Robots <[email protected]>
2+
All rights reserved.
3+
4+
Redistribution and use in source and binary forms, with or without
5+
modification, are permitted provided that the following conditions
6+
are met:
7+
8+
* Redistributions of source code must retain the above copyright
9+
notice, this list of conditions and the following disclaimer.
10+
11+
* Redistributions in binary form must reproduce the above
12+
copyright notice, this list of conditions and the following
13+
disclaimer in the documentation and/or other materials
14+
provided with the distribution.
15+
16+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20+
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

README.md

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# memoires-decrypt
2+
3+
This cross-platform command-line program decrypts journals encrypted with
4+
[Mémoires 4.0][mem] and later. The result of decryption is the original
5+
unencrypted journal (which is an SQLite database file).
6+
7+
The program is freely available under the 2-clause BSD license as an
8+
anti-lock in initiative of [Coding Robots][cr], makers of Mémoires.
9+
We support open standards and data portability.
10+
11+
12+
## Installation
13+
14+
To install the program from sources, first install [Go programming language][go].
15+
Then type the following command in Terminal:
16+
17+
go get github.com/coding-robots/memoires-decrypt
18+
19+
This command will install `memoires-decrypt` into your $GOPATH/bin directory.
20+
21+
22+
## Usage
23+
24+
memoires-decrypt -p="password" -in="encrypted.memoire" out="decrypted.memoire"
25+
26+
where:
27+
28+
-in: encrypted journal file
29+
-out: decrypted SQLite file
30+
-p: password
31+
32+
33+
[mem]: http://www.codingrobots.com/memoires/
34+
[cr]: http://www.codingrobots.com
35+
[go]: http://golang.org

main.go

+164
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
// Copyright 2013 Coding Robots. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// Command memoires-decrypt decrypts journals encrypted with Mémoires 4.0 and later.
6+
package main
7+
8+
import (
9+
"bytes"
10+
"crypto/aes"
11+
"crypto/cipher"
12+
"crypto/subtle"
13+
"errors"
14+
"flag"
15+
"io"
16+
"log"
17+
"os"
18+
19+
"code.google.com/p/go.crypto/scrypt"
20+
"github.com/dchest/blake2b"
21+
)
22+
23+
var (
24+
fPassword = flag.String("p", "", "password")
25+
fInFile = flag.String("in", "", "encrypted journal file")
26+
fOutFile = flag.String("out", "", "decrypted SQLite file")
27+
)
28+
29+
func main() {
30+
flag.Parse()
31+
log.SetFlags(0)
32+
if *fPassword == "" || *fInFile == "" || *fOutFile == "" {
33+
flag.Usage()
34+
return
35+
}
36+
inf, err := os.Open(*fInFile)
37+
if err != nil {
38+
log.Fatal(err)
39+
}
40+
defer inf.Close()
41+
outf, err := os.Create(*fOutFile)
42+
if err != nil {
43+
log.Fatal(err)
44+
}
45+
defer outf.Close()
46+
err = Decrypt(inf, outf, []byte(*fPassword))
47+
if err != nil {
48+
os.Remove(*fOutFile)
49+
log.Fatal(err)
50+
}
51+
}
52+
53+
var (
54+
ErrWrongFormat = errors.New("wrong file format")
55+
ErrUnsupportedVersion = errors.New("unsupported version")
56+
ErrWrongPassword = errors.New("wrong password")
57+
ErrCorrupted = errors.New("file corrupted")
58+
)
59+
60+
const headerSize = 8 /*id*/ + 1 /*ver*/ + 1 /*logN*/ + 1 /*logR*/ + 1 /*logP*/ + 32 /*salt*/ + 16 /*iv*/ + 32 /*hash*/ + 32 /*header MAC*/
61+
62+
func Decrypt(r io.Reader, w io.Writer, password []byte) error {
63+
// Read the whole input file into memory.
64+
var buf bytes.Buffer
65+
_, err := io.Copy(&buf, r)
66+
if err != nil {
67+
return err
68+
}
69+
input := buf.Bytes()
70+
header := input[:headerSize]
71+
content := input[headerSize : len(input)-32]
72+
73+
// Check ID string.
74+
if string(header[:8]) != "MEM_encr" {
75+
return ErrWrongFormat
76+
}
77+
78+
// Check format version.
79+
if header[8] != 1 {
80+
return ErrUnsupportedVersion
81+
}
82+
83+
// Read KDF parameters.
84+
logN := header[9]
85+
logR := header[10]
86+
logP := header[11]
87+
salt := header[12:44]
88+
89+
// Read IV for encryption.
90+
iv := header[44:60]
91+
92+
// Check header hash.
93+
curhash := blake2b.Sum256(header[:60])
94+
if subtle.ConstantTimeCompare(curhash[:], header[60:92]) != 1 {
95+
return ErrCorrupted
96+
}
97+
98+
// Derive keys.
99+
keys, err := deriveKeys(password, salt, logN, logR, logP)
100+
if err != nil {
101+
return err
102+
}
103+
mackey := keys[0:32]
104+
enckey := keys[32:64]
105+
106+
// Check header MAC.
107+
h := blake2b.NewMAC(32, mackey)
108+
h.Write(header[:92])
109+
if subtle.ConstantTimeCompare(h.Sum(nil), header[92:124]) != 1 {
110+
return ErrWrongPassword
111+
}
112+
113+
// Check content MAC.
114+
h.Reset()
115+
h.Write(input[:len(input)-32])
116+
if subtle.ConstantTimeCompare(h.Sum(nil), input[len(input)-32:]) != 1 {
117+
return ErrCorrupted
118+
}
119+
120+
// Decrypt.
121+
if len(content)%aes.BlockSize != 0 {
122+
return ErrCorrupted
123+
}
124+
a, err := aes.NewCipher(enckey)
125+
if err != nil {
126+
panic(err.Error())
127+
}
128+
out := make([]byte, len(content))
129+
dec := cipher.NewCBCDecrypter(a, iv)
130+
dec.CryptBlocks(out, content)
131+
132+
// Strip padding.
133+
n := out[len(out)-1]
134+
if n > aes.BlockSize {
135+
return ErrCorrupted
136+
}
137+
out = out[:len(out)-int(n)]
138+
139+
nw, err := w.Write(out)
140+
if err != nil {
141+
return err
142+
}
143+
if nw != len(out) {
144+
return io.ErrShortWrite
145+
}
146+
return nil
147+
}
148+
149+
func deriveKeys(password, salt []byte, logN, logR, logP uint8) ([]byte, error) {
150+
if logN > 32 {
151+
return nil, errors.New("logN is too large")
152+
}
153+
if logR > 6 {
154+
return nil, errors.New("logR is too large")
155+
}
156+
if logP > 6 {
157+
return nil, errors.New("logP is too large")
158+
}
159+
N := int(1 << uint(logN))
160+
r := int(1 << uint(logR))
161+
p := int(1 << uint(logP))
162+
log.Print(N, r, p)
163+
return scrypt.Key(password, salt, N, r, p, 64)
164+
}

0 commit comments

Comments
 (0)