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
|
||||
- [ ] One time auth
|
||||
- [ ] Padding
|
||||
- [?] Semi-automatic memory wiping.
|
||||
|
||||
☑ Indicate specs are compared against test vectors from another source.
|
||||
|
||||
|
@ -111,7 +112,7 @@ public_key.verify_detached message, signature
|
|||
|
||||
### Secret Key Encryption
|
||||
```crystal
|
||||
key = Cox::SecretKey.random
|
||||
key = Cox::SecretKey.new
|
||||
|
||||
message = "foobar"
|
||||
encrypted, nonce = key.encrypt_easy message
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
require "../spec_helper"
|
||||
|
||||
describe Cox::SecretKey do
|
||||
describe Cox::SecretBox do
|
||||
it "encrypts/decrypts" do
|
||||
key = Cox::SecretKey.random
|
||||
key = Cox::SecretBox.new
|
||||
|
||||
message = "foobar"
|
||||
encrypted, nonce = key.encrypt_easy message
|
31
src/cox.cr
31
src/cox.cr
|
@ -8,37 +8,14 @@ module Cox
|
|||
class DecryptionFailed < Error
|
||||
end
|
||||
end
|
||||
|
||||
def self.memzero(bytes : Bytes)
|
||||
LibSodium.sodium_memzero bytes, bytes.bytesize
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
abort "Failed to init libsodium"
|
||||
end
|
||||
|
|
|
@ -2,8 +2,13 @@ require "../lib_sodium"
|
|||
|
||||
module Cox::CryptoBox
|
||||
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)
|
||||
# TODO: precompute using crypto_box_beforenm
|
||||
end
|
||||
|
||||
def encrypt_easy(src)
|
||||
|
@ -25,8 +30,5 @@ module Cox::CryptoBox
|
|||
end
|
||||
|
||||
# TODO detached
|
||||
def close
|
||||
# TODO: wipe state
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,6 +2,7 @@ require "../lib_sodium"
|
|||
|
||||
module Cox::CryptoBox
|
||||
class PublicKey < Key
|
||||
include Wipe
|
||||
KEY_SIZE = LibSodium::PUBLIC_KEY_SIZE
|
||||
|
||||
getter bytes : Bytes
|
||||
|
|
|
@ -2,6 +2,7 @@ require "../lib_sodium"
|
|||
|
||||
module Cox::CryptoBox
|
||||
class SecretKey < Key
|
||||
include Wipe
|
||||
KEY_SIZE = LibSodium::SECRET_KEY_SIZE
|
||||
MAC_SIZE = LibSodium::MAC_SIZE
|
||||
|
||||
|
@ -24,11 +25,12 @@ module Cox::CryptoBox
|
|||
@public_key = PublicKey.new pkey
|
||||
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
|
||||
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)
|
||||
pa = pair public_key
|
||||
begin
|
||||
|
|
|
@ -35,6 +35,7 @@ module Cox
|
|||
fun crypto_generichash_blake2b_keybytes_max : LibC::SizeT
|
||||
fun crypto_generichash_blake2b_saltbytes : LibC::SizeT
|
||||
fun crypto_generichash_blake2b_personalbytes : LibC::SizeT
|
||||
fun sodium_memzero(Pointer(LibC::UChar), LibC::SizeT) : Nil
|
||||
|
||||
PUBLIC_KEY_SIZE = crypto_box_publickeybytes()
|
||||
SECRET_KEY_SIZE = crypto_box_secretkeybytes()
|
||||
|
|
|
@ -1,22 +1,35 @@
|
|||
require "./lib_sodium"
|
||||
|
||||
module Cox
|
||||
class SecretKey < Key
|
||||
property bytes : Bytes
|
||||
|
||||
# [https://libsodium.gitbook.io/doc/secret-key_cryptography](https://libsodium.gitbook.io/doc/secret-key_cryptography)
|
||||
#
|
||||
# ```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
|
||||
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)
|
||||
if bytes.bytesize != KEY_SIZE
|
||||
raise ArgumentError.new("Secret key must be #{KEY_SIZE} bytes, got #{bytes.bytesize}")
|
||||
end
|
||||
end
|
||||
|
||||
def self.random
|
||||
new Random::Secure.random_bytes(KEY_SIZE)
|
||||
end
|
||||
|
||||
def encrypt_easy(data)
|
||||
encrypt_easy data.to_slice
|
||||
end
|
|
@ -2,6 +2,7 @@ require "../lib_sodium"
|
|||
|
||||
module Cox
|
||||
class Sign::PublicKey < Key
|
||||
include Wipe
|
||||
KEY_SIZE = LibSodium::PUBLIC_SIGN_SIZE
|
||||
|
||||
getter bytes : Bytes
|
||||
|
|
|
@ -2,6 +2,7 @@ require "../lib_sodium"
|
|||
|
||||
module Cox
|
||||
class Sign::SecretKey < Cox::Key
|
||||
include Wipe
|
||||
KEY_SIZE = LibSodium::SECRET_SIGN_SIZE
|
||||
|
||||
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