Breaking API changes:
SecretKey renamed to SecretBox. Start of automatic wiping. Documentation additions and corrections.master
parent
a02c54f4a7
commit
848cf3e3e2
|
@ -40,6 +40,7 @@ Updated Crystal bindings for the [libsodium API](https://libsodium.gitbook.io/do
|
||||||
- [x] ChaCha20
|
- [x] ChaCha20
|
||||||
- [ ] One time auth
|
- [ ] One time auth
|
||||||
- [ ] Padding
|
- [ ] Padding
|
||||||
|
- [?] Semi-automatic memory wiping.
|
||||||
|
|
||||||
☑ Indicate specs are compared against test vectors from another source.
|
☑ Indicate specs are compared against test vectors from another source.
|
||||||
|
|
||||||
|
@ -111,7 +112,7 @@ public_key.verify_detached message, signature
|
||||||
|
|
||||||
### Secret Key Encryption
|
### Secret Key Encryption
|
||||||
```crystal
|
```crystal
|
||||||
key = Cox::SecretKey.random
|
key = Cox::SecretKey.new
|
||||||
|
|
||||||
message = "foobar"
|
message = "foobar"
|
||||||
encrypted, nonce = key.encrypt_easy message
|
encrypted, nonce = key.encrypt_easy message
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
require "../spec_helper"
|
require "../spec_helper"
|
||||||
|
|
||||||
describe Cox::SecretKey do
|
describe Cox::SecretBox do
|
||||||
it "encrypts/decrypts" do
|
it "encrypts/decrypts" do
|
||||||
key = Cox::SecretKey.random
|
key = Cox::SecretBox.new
|
||||||
|
|
||||||
message = "foobar"
|
message = "foobar"
|
||||||
encrypted, nonce = key.encrypt_easy message
|
encrypted, nonce = key.encrypt_easy message
|
31
src/cox.cr
31
src/cox.cr
|
@ -8,37 +8,14 @@ module Cox
|
||||||
class DecryptionFailed < Error
|
class DecryptionFailed < Error
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.memzero(bytes : Bytes)
|
||||||
|
LibSodium.sodium_memzero bytes, bytes.bytesize
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
require "./cox/**"
|
require "./cox/**"
|
||||||
|
|
||||||
module Cox
|
|
||||||
def self.encrypt(data, nonce : Nonce, recipient_public_key : CryptoBox::PublicKey, sender_secret_key : CryptoBox::SecretKey)
|
|
||||||
data_buffer = data.to_slice
|
|
||||||
data_size = data_buffer.bytesize
|
|
||||||
output_buffer = Bytes.new(data_buffer.bytesize + LibSodium::MAC_SIZE)
|
|
||||||
if LibSodium.crypto_box_easy(output_buffer.to_slice, data_buffer, data_size, nonce.to_slice, recipient_public_key.to_slice, sender_secret_key.to_slice) != 0
|
|
||||||
raise Error.new("crypto_box_easy")
|
|
||||||
end
|
|
||||||
output_buffer
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.encrypt(data, recipient_public_key : CryptoBox::PublicKey, sender_secret_key : CryptoBox::SecretKey)
|
|
||||||
nonce = Nonce.new
|
|
||||||
{nonce, encrypt(data, nonce, recipient_public_key, sender_secret_key)}
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.decrypt(data, nonce : Nonce, sender_public_key : CryptoBox::PublicKey, recipient_secret_key : CryptoBox::SecretKey)
|
|
||||||
data_buffer = data.to_slice
|
|
||||||
data_size = data_buffer.bytesize
|
|
||||||
output_buffer = Bytes.new(data_buffer.bytesize - LibSodium::MAC_SIZE)
|
|
||||||
if LibSodium.crypto_box_open_easy(output_buffer.to_slice, data_buffer.to_slice, data_size, nonce.to_slice, sender_public_key.to_slice, recipient_secret_key.to_slice) != 0
|
|
||||||
raise Error::DecryptionFailed.new("crypto_box_open_easy")
|
|
||||||
end
|
|
||||||
output_buffer
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if Cox::LibSodium.sodium_init == -1
|
if Cox::LibSodium.sodium_init == -1
|
||||||
abort "Failed to init libsodium"
|
abort "Failed to init libsodium"
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,8 +2,13 @@ require "../lib_sodium"
|
||||||
|
|
||||||
module Cox::CryptoBox
|
module Cox::CryptoBox
|
||||||
class Pair
|
class Pair
|
||||||
# TODO: precompute using crypto_box_beforenm
|
include Wipe
|
||||||
|
|
||||||
|
# BUG: precompute size
|
||||||
|
@bytes = Bytes.new(1)
|
||||||
|
|
||||||
def initialize(@secret_key : SecretKey, @public_key : PublicKey)
|
def initialize(@secret_key : SecretKey, @public_key : PublicKey)
|
||||||
|
# TODO: precompute using crypto_box_beforenm
|
||||||
end
|
end
|
||||||
|
|
||||||
def encrypt_easy(src)
|
def encrypt_easy(src)
|
||||||
|
@ -25,8 +30,5 @@ module Cox::CryptoBox
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO detached
|
# TODO detached
|
||||||
def close
|
|
||||||
# TODO: wipe state
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,6 +2,7 @@ require "../lib_sodium"
|
||||||
|
|
||||||
module Cox::CryptoBox
|
module Cox::CryptoBox
|
||||||
class PublicKey < Key
|
class PublicKey < Key
|
||||||
|
include Wipe
|
||||||
KEY_SIZE = LibSodium::PUBLIC_KEY_SIZE
|
KEY_SIZE = LibSodium::PUBLIC_KEY_SIZE
|
||||||
|
|
||||||
getter bytes : Bytes
|
getter bytes : Bytes
|
||||||
|
|
|
@ -2,6 +2,7 @@ require "../lib_sodium"
|
||||||
|
|
||||||
module Cox::CryptoBox
|
module Cox::CryptoBox
|
||||||
class SecretKey < Key
|
class SecretKey < Key
|
||||||
|
include Wipe
|
||||||
KEY_SIZE = LibSodium::SECRET_KEY_SIZE
|
KEY_SIZE = LibSodium::SECRET_KEY_SIZE
|
||||||
MAC_SIZE = LibSodium::MAC_SIZE
|
MAC_SIZE = LibSodium::MAC_SIZE
|
||||||
|
|
||||||
|
@ -24,11 +25,12 @@ module Cox::CryptoBox
|
||||||
@public_key = PublicKey.new pkey
|
@public_key = PublicKey.new pkey
|
||||||
end
|
end
|
||||||
|
|
||||||
def pair(public_key)
|
# Return a Pair containing a precomputed shared secret for use with encryption/decryption.
|
||||||
|
def pair(public_key) : Pair
|
||||||
Pair.new self, public_key
|
Pair.new self, public_key
|
||||||
end
|
end
|
||||||
|
|
||||||
# Create a new pair and automatically close when exiting the block.
|
# Create a new pair and automatically close when the block exits.
|
||||||
def pair(public_key)
|
def pair(public_key)
|
||||||
pa = pair public_key
|
pa = pair public_key
|
||||||
begin
|
begin
|
||||||
|
|
|
@ -35,6 +35,7 @@ module Cox
|
||||||
fun crypto_generichash_blake2b_keybytes_max : LibC::SizeT
|
fun crypto_generichash_blake2b_keybytes_max : LibC::SizeT
|
||||||
fun crypto_generichash_blake2b_saltbytes : LibC::SizeT
|
fun crypto_generichash_blake2b_saltbytes : LibC::SizeT
|
||||||
fun crypto_generichash_blake2b_personalbytes : LibC::SizeT
|
fun crypto_generichash_blake2b_personalbytes : LibC::SizeT
|
||||||
|
fun sodium_memzero(Pointer(LibC::UChar), LibC::SizeT) : Nil
|
||||||
|
|
||||||
PUBLIC_KEY_SIZE = crypto_box_publickeybytes()
|
PUBLIC_KEY_SIZE = crypto_box_publickeybytes()
|
||||||
SECRET_KEY_SIZE = crypto_box_secretkeybytes()
|
SECRET_KEY_SIZE = crypto_box_secretkeybytes()
|
||||||
|
|
|
@ -1,22 +1,35 @@
|
||||||
require "./lib_sodium"
|
require "./lib_sodium"
|
||||||
|
|
||||||
module Cox
|
module Cox
|
||||||
class SecretKey < Key
|
# [https://libsodium.gitbook.io/doc/secret-key_cryptography](https://libsodium.gitbook.io/doc/secret-key_cryptography)
|
||||||
property bytes : Bytes
|
#
|
||||||
|
# ```crystal
|
||||||
|
# key = Cox::SecretKey.new
|
||||||
|
# message = "foobar"
|
||||||
|
# encrypted, nonce = key.encrypt_easy message
|
||||||
|
#
|
||||||
|
# # On the other side.
|
||||||
|
# key = Cox::SecretKey.new key
|
||||||
|
# message = key.decrypt_easy encrypted, nonce
|
||||||
|
# ```
|
||||||
|
class SecretBox < Key
|
||||||
KEY_SIZE = LibSodium::SECRET_KEY_SIZE
|
KEY_SIZE = LibSodium::SECRET_KEY_SIZE
|
||||||
MAC_SIZE = LibSodium::MAC_SIZE
|
MAC_SIZE = LibSodium::MAC_SIZE
|
||||||
|
|
||||||
|
property bytes : Bytes
|
||||||
|
|
||||||
|
# Generate a new random key.
|
||||||
|
def initialize
|
||||||
|
@bytes = Random::Secure.random_bytes(KEY_SIZE)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Use an existing key from bytes.
|
||||||
def initialize(@bytes : Bytes)
|
def initialize(@bytes : Bytes)
|
||||||
if bytes.bytesize != KEY_SIZE
|
if bytes.bytesize != KEY_SIZE
|
||||||
raise ArgumentError.new("Secret key must be #{KEY_SIZE} bytes, got #{bytes.bytesize}")
|
raise ArgumentError.new("Secret key must be #{KEY_SIZE} bytes, got #{bytes.bytesize}")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.random
|
|
||||||
new Random::Secure.random_bytes(KEY_SIZE)
|
|
||||||
end
|
|
||||||
|
|
||||||
def encrypt_easy(data)
|
def encrypt_easy(data)
|
||||||
encrypt_easy data.to_slice
|
encrypt_easy data.to_slice
|
||||||
end
|
end
|
|
@ -2,6 +2,7 @@ require "../lib_sodium"
|
||||||
|
|
||||||
module Cox
|
module Cox
|
||||||
class Sign::PublicKey < Key
|
class Sign::PublicKey < Key
|
||||||
|
include Wipe
|
||||||
KEY_SIZE = LibSodium::PUBLIC_SIGN_SIZE
|
KEY_SIZE = LibSodium::PUBLIC_SIGN_SIZE
|
||||||
|
|
||||||
getter bytes : Bytes
|
getter bytes : Bytes
|
||||||
|
|
|
@ -2,6 +2,7 @@ require "../lib_sodium"
|
||||||
|
|
||||||
module Cox
|
module Cox
|
||||||
class Sign::SecretKey < Cox::Key
|
class Sign::SecretKey < Cox::Key
|
||||||
|
include Wipe
|
||||||
KEY_SIZE = LibSodium::SECRET_SIGN_SIZE
|
KEY_SIZE = LibSodium::SECRET_SIGN_SIZE
|
||||||
|
|
||||||
getter bytes : Bytes
|
getter bytes : Bytes
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
module Cox::Wipe
|
||||||
|
@closed = false
|
||||||
|
|
||||||
|
def close
|
||||||
|
return if @closed
|
||||||
|
wipe
|
||||||
|
@closed = true
|
||||||
|
end
|
||||||
|
|
||||||
|
protected def wipe
|
||||||
|
return if @closed
|
||||||
|
Cox.memzero @bytes
|
||||||
|
end
|
||||||
|
|
||||||
|
def finalize
|
||||||
|
wipe # Don't call close. May be overridden with calls unsafe within finalize.
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue