diff --git a/build.sh b/build.sh index 6f73f25..cab3021 100755 --- a/build.sh +++ b/build.sh @@ -12,7 +12,7 @@ mkdir -p $IOS_OUT printf "\e[0;32mStart Building iOS framework .. Location: ${IOS_OUT} \033[0m\n\n" PACKAGE_PATH=github.com/ProtonMail/go-pm-crypto -gomobile bind -target ios -o ${IOS_OUT}/pmcrypto.framework $PACKAGE_PATH/crypto $PACKAGE_PATH/armor $PACKAGE_PATH/constants $PACKAGE_PATH/key $PACKAGE_PATH/models +gomobile bind -target ios -o ${IOS_OUT}/Crypto.framework $PACKAGE_PATH/crypto $PACKAGE_PATH/armor $PACKAGE_PATH/constants $PACKAGE_PATH/key $PACKAGE_PATH/models printf "\e[0;32mStart Building Android lib .. Location: ${ANDROID_OUT} \033[0m\n\n" diff --git a/crypto/subtle.go b/crypto/subtle.go new file mode 100644 index 0000000..eca3c30 --- /dev/null +++ b/crypto/subtle.go @@ -0,0 +1,31 @@ +package crypto + +import ( + "crypto/aes" + "crypto/cipher" + + "golang.org/x/crypto/scrypt" +) + +// EncryptWithoutIntegrity encrypts data with AES-CTR. Note: this encryption mode is not secure when stored/sent on an untrusted medium. +func EncryptWithoutIntegrity(key, input, iv []byte) (output []byte, err error) { + var block cipher.Block + if block, err = aes.NewCipher(key); err != nil { + return + } + output = make([]byte, len(input)) + stream := cipher.NewCTR(block, iv) + stream.XORKeyStream(output, input) + return +} + +// DecryptWithoutIntegrity decrypts data encrypted with AES-CTR. +func DecryptWithoutIntegrity(key, input, iv []byte) ([]byte, error) { + // AES-CTR decryption is identical to encryption. + return EncryptWithoutIntegrity(key, input, iv) +} + +// DeriveKey derives a key from a password using scrypt. +func DeriveKey(password string, salt []byte) ([]byte, error) { + return scrypt.Key([]byte(password), salt, 32768, 8, 1, 32) +} diff --git a/crypto/subtle_test.go b/crypto/subtle_test.go new file mode 100644 index 0000000..1c8cbc7 --- /dev/null +++ b/crypto/subtle_test.go @@ -0,0 +1,36 @@ +package crypto + +import ( + "encoding/hex" + "testing" +) + +func TestSubtle_EncryptWithoutIntegrity(t *testing.T) { + key, _ := hex.DecodeString("9469cccfc8a8d005247f39fa3e5b35a97db456cecf18deac6d84364d0818d763") + plaintext := []byte("some plaintext") + iv, _ := hex.DecodeString("c828f258a76aad7bc828f258a76aad7b") + + ciphertext, _ := EncryptWithoutIntegrity(key, plaintext, iv) + if hex.EncodeToString(ciphertext) != "14697192f7e112fc88d83380693f" { + t.Fatal("EncryptWithoutIntegrity returned unexpected result") + } +} + +func TestSubtle_DecryptWithoutIntegrity(t *testing.T) { + key, _ := hex.DecodeString("9469cccfc8a8d005247f39fa3e5b35a97db456cecf18deac6d84364d0818d763") + ciphertext, _ := hex.DecodeString("14697192f7e112fc88d83380693f") + iv, _ := hex.DecodeString("c828f258a76aad7bc828f258a76aad7b") + + plaintext, _ := DecryptWithoutIntegrity(key, ciphertext, iv) + if string(plaintext) != "some plaintext" { + t.Fatal("DecryptWithoutIntegrity returned unexpected result") + } +} + +func TestSubtle_DeriveKey(t *testing.T) { + salt, _ := hex.DecodeString("c828f258a76aad7b") + dk, _ := DeriveKey("some password", salt) + if hex.EncodeToString(dk) != "9469cccfc8a8d005247f39fa3e5b35a97db456cecf18deac6d84364d0818d763" { + t.Fatal("DeriveKey returned unexpected result") + } +}