Skip to content
Merged
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions cmd/cql-adapter/storage/sqlite3.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"math/rand"
"os"
"path/filepath"

// Import sqlite3 manually.
_ "github.com/CovenantSQL/go-sqlite3-encrypt"
)
Expand Down
1 change: 1 addition & 0 deletions cmd/cql-faucet/persistence.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/CovenantSQL/CovenantSQL/conf"
"github.com/CovenantSQL/CovenantSQL/utils/log"
uuid "github.com/satori/go.uuid"

// Load sqlite3 database driver.
_ "github.com/CovenantSQL/go-sqlite3-encrypt"
)
Expand Down
1 change: 1 addition & 0 deletions crypto/hash/hashfuncs.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package hash
import (
"encoding/binary"
"hash/fnv"

// "crypto/sha256" benchmark is at least 10% faster on
// i7-4870HQ CPU @ 2.50GHz than "github.com/minio/sha256-simd"
"crypto/sha256"
Expand Down
73 changes: 52 additions & 21 deletions crypto/kms/privatekeystore.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@ import (
"io/ioutil"
"os"

"github.com/btcsuite/btcutil/base58"

"github.com/CovenantSQL/CovenantSQL/conf"
"github.com/CovenantSQL/CovenantSQL/crypto/asymmetric"
"github.com/CovenantSQL/CovenantSQL/crypto/hash"
"github.com/CovenantSQL/CovenantSQL/crypto/symmetric"
"github.com/CovenantSQL/CovenantSQL/utils/log"
"github.com/btcsuite/btcutil/base58"
)

var (
Expand All @@ -41,62 +42,92 @@ var (
ErrInvalidBase58Checksum = errors.New("invalid base58 checksum")
// PrivateKeyStoreVersion defines the private key version byte.
PrivateKeyStoreVersion byte = 0x23
// oldPrivateKDFSalt is the old KDF salt for private key encryption
oldPrivateKDFSalt = "auxten-key-salt-auxten"
// privateKDFSalt is the KDF salt for private key encryption
privateKDFSalt = []byte{
0xC0, 0x4E, 0xA4, 0x71, 0x49, 0x65, 0x41, 0x31,
0x79, 0x4b, 0x6a, 0x70, 0x2f, 0x39, 0x45, 0x43,
}
)

// LoadPrivateKey loads private key from keyFilePath, and verifies the hash
// head
func LoadPrivateKey(keyFilePath string, masterKey []byte) (key *asymmetric.PrivateKey, err error) {
var (
isBinaryKey bool
decData []byte
)
fileContent, err := ioutil.ReadFile(keyFilePath)
if err != nil {
log.WithField("path", keyFilePath).WithError(err).Error("read key file failed")
return
}

// It's very impossible to get an raw private key base58 decodable.
// So if it's not base58 decodable we just make fileContent the encData
encData, version, err := base58.CheckDecode(string(fileContent))
switch err {
case base58.ErrChecksum:
return

case base58.ErrInvalidFormat:
// be compatible with the original binary private key format
isBinaryKey = true
encData = fileContent
}

if version != 0 && version != PrivateKeyStoreVersion {
return nil, ErrInvalidBase58Version
}

decData, err := symmetric.DecryptWithPassword(encData, masterKey)
if err != nil {
log.Error("decrypt private key error")
return
}
if isBinaryKey {
decData, err = symmetric.DecryptWithPassword(encData, masterKey, []byte(oldPrivateKDFSalt))
if err != nil {
log.Error("decrypt private key error")
return
}

// sha256 + privateKey
if len(decData) != hash.HashBSize+asymmetric.PrivateKeyBytesLen {
log.WithFields(log.Fields{
"expected": hash.HashBSize + asymmetric.PrivateKeyBytesLen,
"actual": len(decData),
}).Error("wrong private key file size")
return nil, ErrNotKeyFile
}
// sha256 + privateKey
if len(decData) != hash.HashBSize+asymmetric.PrivateKeyBytesLen {
log.WithFields(log.Fields{
"expected": hash.HashBSize + asymmetric.PrivateKeyBytesLen,
"actual": len(decData),
}).Error("wrong binary private key file size")
return nil, ErrNotKeyFile
}

computedHash := hash.DoubleHashB(decData[hash.HashBSize:])
if !bytes.Equal(computedHash, decData[:hash.HashBSize]) {
return nil, ErrHashNotMatch
}
key, _ = asymmetric.PrivKeyFromBytes(decData[hash.HashBSize:])
} else {
decData, err = symmetric.DecryptWithPassword(encData, masterKey, privateKDFSalt)
if err != nil {
log.Error("decrypt private key error")
return
}

computedHash := hash.DoubleHashB(decData[hash.HashBSize:])
if !bytes.Equal(computedHash, decData[:hash.HashBSize]) {
return nil, ErrHashNotMatch
// privateKey
if len(decData) != asymmetric.PrivateKeyBytesLen {
log.WithFields(log.Fields{
"expected": asymmetric.PrivateKeyBytesLen,
"actual": len(decData),
}).Error("wrong base58 private key file size")
return nil, ErrNotKeyFile
}
key, _ = asymmetric.PrivKeyFromBytes(decData)
}

key, _ = asymmetric.PrivKeyFromBytes(decData[hash.HashBSize:])
return
}

// SavePrivateKey saves private key with its hash on the head to keyFilePath,
// default perm is 0600
func SavePrivateKey(keyFilePath string, key *asymmetric.PrivateKey, masterKey []byte) (err error) {
serializedKey := key.Serialize()
keyHash := hash.DoubleHashB(serializedKey)
rawData := append(keyHash, serializedKey...)
encKey, err := symmetric.EncryptWithPassword(rawData, masterKey)
encKey, err := symmetric.EncryptWithPassword(serializedKey, masterKey, privateKDFSalt)
if err != nil {
return
}
Expand Down
11 changes: 6 additions & 5 deletions crypto/kms/privatekeystore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
const (
privateKeyPath = "./.testprivatekey"
password = "auxten"
salt = "auxten-key-salt-auxten"
)

func TestSaveLoadPrivateKey(t *testing.T) {
Expand Down Expand Up @@ -83,15 +84,15 @@ func TestLoadPrivateKey(t *testing.T) {
})
Convey("not key file2", t, func() {
defer os.Remove("./.notkey")
enc, _ := symmetric.EncryptWithPassword([]byte("aa"), []byte(password))
enc, _ := symmetric.EncryptWithPassword([]byte("aa"), []byte(password), []byte(salt))
ioutil.WriteFile("./.notkey", enc, 0600)
lk, err := LoadPrivateKey("./.notkey", []byte(password))
So(err, ShouldEqual, ErrNotKeyFile)
So(lk, ShouldBeNil)
})
Convey("hash not match", t, func() {
defer os.Remove("./.HashNotMatch")
enc, _ := symmetric.EncryptWithPassword(bytes.Repeat([]byte("a"), 64), []byte(password))
enc, _ := symmetric.EncryptWithPassword(bytes.Repeat([]byte("a"), 64), []byte(password), []byte(salt))
ioutil.WriteFile("./.HashNotMatch", enc, 0600)
lk, err := LoadPrivateKey("./.HashNotMatch", []byte(password))
So(err, ShouldEqual, ErrHashNotMatch)
Expand All @@ -105,7 +106,7 @@ func TestLoadPrivateKey(t *testing.T) {
serializedKey := privateKey.Serialize()
keyHash := hash.DoubleHashB(serializedKey)
rawData := append(keyHash, serializedKey...)
encKey, _ := symmetric.EncryptWithPassword(rawData, []byte(password))
encKey, _ := symmetric.EncryptWithPassword(rawData, []byte(password), []byte(salt))
invalidBase58EncKey := base58.CheckEncode(encKey, invalidPrivateKeyStoreVersion)
ioutil.WriteFile("./.Base58VersionNotMatch", []byte(invalidBase58EncKey), 0600)
lk, err := LoadPrivateKey("./.Base58VersionNotMatch", []byte(password))
Expand Down Expand Up @@ -154,14 +155,14 @@ func TestInitLocalKeyPair(t *testing.T) {
func TestInitLocalKeyPair_error(t *testing.T) {
Convey("hash not match", t, func() {
defer os.Remove("./.HashNotMatch")
enc, _ := symmetric.EncryptWithPassword(bytes.Repeat([]byte("a"), 64), []byte(password))
enc, _ := symmetric.EncryptWithPassword(bytes.Repeat([]byte("a"), 64), []byte(password), []byte(salt))
ioutil.WriteFile("./.HashNotMatch", enc, 0600)
err := InitLocalKeyPair("./.HashNotMatch", []byte(password))
So(err, ShouldEqual, ErrHashNotMatch)
})
Convey("ErrNotKeyFile", t, func() {
defer os.Remove("./.ErrNotKeyFile")
enc, _ := symmetric.EncryptWithPassword(bytes.Repeat([]byte("a"), 65), []byte(password))
enc, _ := symmetric.EncryptWithPassword(bytes.Repeat([]byte("a"), 65), []byte(password), []byte(salt))
ioutil.WriteFile("./.ErrNotKeyFile", enc, 0600)
err := InitLocalKeyPair("./.ErrNotKeyFile", []byte(password))
So(err, ShouldEqual, ErrNotKeyFile)
Expand Down
16 changes: 6 additions & 10 deletions crypto/symmetric/aes.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,27 +28,23 @@ import (
"github.com/CovenantSQL/CovenantSQL/crypto/hash"
)

const (
keySalt = "auxten-key-salt-auxten"
)

var (
// ErrInputSize indicates cipher data size is not expected,
// maybe data is not encrypted by EncryptWithPassword in this package
ErrInputSize = errors.New("cipher data size not match")
)

// keyDerivation does sha256 twice to password
func keyDerivation(password []byte) (out []byte) {
return hash.DoubleHashB(append(password, keySalt...))
func keyDerivation(password []byte, salt []byte) (out []byte) {
return hash.DoubleHashB(append(password, salt...))
}

// EncryptWithPassword encrypts data with given password, iv will be placed
// at head of cipher data
func EncryptWithPassword(in, password []byte) (out []byte, err error) {
func EncryptWithPassword(in, password []byte, salt []byte) (out []byte, err error) {
// keyE will be 256 bits, so aes.NewCipher(keyE) will return
// AES-256 Cipher.
keyE := keyDerivation(password)
keyE := keyDerivation(password, salt)
paddedIn := crypto.AddPKCSPadding(in)
// IV + padded cipher data
out = make([]byte, aes.BlockSize+len(paddedIn))
Expand All @@ -70,8 +66,8 @@ func EncryptWithPassword(in, password []byte) (out []byte, err error) {
}

// DecryptWithPassword decrypts data with given password
func DecryptWithPassword(in, password []byte) (out []byte, err error) {
keyE := keyDerivation(password)
func DecryptWithPassword(in, password []byte, salt []byte) (out []byte, err error) {
keyE := keyDerivation(password, salt)
// IV + padded cipher data == (n + 1 + 1) * aes.BlockSize
if len(in)%aes.BlockSize != 0 || len(in)/aes.BlockSize < 2 {
return nil, ErrInputSize
Expand Down
15 changes: 9 additions & 6 deletions crypto/symmetric/aes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,37 +24,40 @@ import (
. "github.com/smartystreets/goconvey/convey"
)

const password = "CovenantSQL.io"
const (
password = "CovenantSQL.io"
salt = "auxten-key-salt-auxten"
)

func TestEncryptDecryptWithPassword(t *testing.T) {
Convey("encrypt & decrypt 0 length bytes with aes256", t, func() {
enc, err := EncryptWithPassword([]byte(nil), []byte(password))
enc, err := EncryptWithPassword([]byte(nil), []byte(password), []byte(salt))
So(enc, ShouldNotBeNil)
So(len(enc), ShouldEqual, 2*aes.BlockSize)
So(err, ShouldBeNil)

dec, err := DecryptWithPassword(enc, []byte(password))
dec, err := DecryptWithPassword(enc, []byte(password), []byte(salt))
So(dec, ShouldNotBeNil)
So(len(dec), ShouldEqual, 0)
So(err, ShouldBeNil)
})

Convey("encrypt & decrypt 1747 length bytes", t, func() {
in := bytes.Repeat([]byte{0xff}, 1747)
enc, err := EncryptWithPassword(in, []byte(password))
enc, err := EncryptWithPassword(in, []byte(password), []byte(salt))
So(enc, ShouldNotBeNil)
So(len(enc), ShouldEqual, (1747/aes.BlockSize+2)*aes.BlockSize)
So(err, ShouldBeNil)

dec, err := DecryptWithPassword(enc, []byte(password))
dec, err := DecryptWithPassword(enc, []byte(password), []byte(salt))
So(dec, ShouldNotBeNil)
So(len(dec), ShouldEqual, 1747)
So(err, ShouldBeNil)
})

Convey("decrypt error length bytes", t, func() {
in := bytes.Repeat([]byte{0xff}, 1747)
dec, err := DecryptWithPassword(in, []byte(password))
dec, err := DecryptWithPassword(in, []byte(password), []byte(salt))
So(dec, ShouldBeNil)
So(err, ShouldEqual, ErrInputSize)
})
Expand Down
1 change: 1 addition & 0 deletions storage/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (

"github.com/CovenantSQL/CovenantSQL/twopc"
"github.com/CovenantSQL/CovenantSQL/utils/log"

// Register CovenantSQL/go-sqlite3-encrypt engine.
_ "github.com/CovenantSQL/go-sqlite3-encrypt"
)
Expand Down
18 changes: 0 additions & 18 deletions utils/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,24 +42,6 @@ func GetProjectSrcDir() string {
return FJ(filepath.Dir(testFile), "../")
}

// Build runs make
func Build() (err error) {
wd := GetProjectSrcDir()
err = os.Chdir(wd)
if err != nil {
log.WithError(err).Error("change working dir failed")
return
}
exec.Command("make", "clean").Run()
cmd := exec.Command("make", "use_all_cores")
output, err := cmd.CombinedOutput()
if err != nil {
log.WithError(err).Error("build failed")
}
log.Debugf("build output info: %#v", string(output))
return
}

// RunCommand runs a command and capture its output to a log file,
// if toStd is true also output to stdout and stderr
func RunCommand(bin string, args []string, processName string, workingDir string, logDir string, toStd bool) (err error) {
Expand Down
7 changes: 0 additions & 7 deletions utils/exec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,6 @@ var (
logDir = FJ(testWorkingDir, "./log/")
)

func TestBuild(t *testing.T) {
Convey("build", t, func() {
log.SetLevel(log.DebugLevel)
So(Build(), ShouldBeNil)
})
}

func TestRunServer(t *testing.T) {
Convey("build", t, func() {
log.SetLevel(log.DebugLevel)
Expand Down
1 change: 1 addition & 0 deletions xenomint/xxx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"os"
"path"
"sync/atomic"

//"runtime/trace"
"sync"
"syscall"
Expand Down