Add libsodium kdf.

master
Didactic Drunk 2019-05-28 14:31:31 -07:00
parent e82d4416b4
commit a358929e62
4 changed files with 95 additions and 0 deletions

View File

@ -55,6 +55,15 @@ signature = Cox.sign_detached(message, signing_pair.secret)
Cox.verify_detached(signature, message, signing_pair.public) # => true Cox.verify_detached(signature, message, signing_pair.public) # => true
``` ```
# Key derivation
kdf = Cox::Kdf.new
# kdf.derive(8_byte_context, subkey_size, subkey_id)
subkey1 = kdf.derive "context1", 16, 0
subkey2 = kdf.derive "context1", 16, 1
subkey3 = kdf.derive "context2", 32, 0
subkey4 = kdf.derive "context2", 64, 1
## Contributing ## Contributing
1. Fork it ( https://github.com/andrewhamon/cox/fork ) 1. Fork it ( https://github.com/andrewhamon/cox/fork )

26
spec/cox/kdf_spec.cr Normal file
View File

@ -0,0 +1,26 @@
require "../spec_helper"
CONTEXT = "8_bytess"
describe Cox::Kdf do
it "generates master key" do
kdf1 = Cox::Kdf.new
# verify loading saved key
kdf2 = Cox::Kdf.from_base64 kdf1.to_base64
# verify generated subkey's are the same after loading
key1_s1 = kdf1.derive CONTEXT, 16, 0
key2_s1 = kdf2.derive CONTEXT, 16, 0
key1_s1.should eq key2_s1
end
it "generates different keys" do
kdf1 = Cox::Kdf.new
subkey1 = kdf1.derive CONTEXT, 16, 0
subkey2 = kdf1.derive CONTEXT, 16, 1
subkey1.should_not eq subkey2
end
# TODO: test exceptions
end

47
src/cox/kdf.cr Normal file
View File

@ -0,0 +1,47 @@
module Cox
class Kdf
property bytes : Bytes
def initialize(bytes : Bytes)
if bytes.bytesize != LibSodium::KDF_KEY_BYTES
raise ArgumentError.new("bytes must be #{LibSodium::KDF_KEY_BYTES}, got #{bytes.bytesize}")
end
@bytes = bytes
end
def initialize
@bytes = Random::Secure.random_bytes(LibSodium::KDF_KEY_BYTES)
end
# context must be 8 bytes
# subkey_size must be 16..64 bytes as of libsodium 1.0.17
def derive(context, subkey_size, subkey_id = 0)
if context.bytesize != LibSodium::KDF_CONTEXT_BYTES
raise ArgumentError.new("context must be #{LibSodium::KDF_CONTEXT_BYTES}, got #{context.bytesize}")
end
subkey = Bytes.new subkey_size
if (ret = LibSodium.crypto_kdf_derive_from_key(subkey, subkey.bytesize, subkey_id, context, @bytes)) != 0
raise Cox::Error.new("crypto_kdf_derive_from_key returned #{ret} (subkey size is probably out of range)")
end
subkey
end
def pointer
bytes.to_unsafe
end
def pointer(size)
bytes.pointer(size)
end
def to_base64
Base64.encode(bytes)
end
def self.from_base64(encoded_key)
new(Base64.decode(encoded_key))
end
end
end

View File

@ -10,6 +10,8 @@ module Cox
fun crypto_sign_publickeybytes() : LibC::SizeT fun crypto_sign_publickeybytes() : LibC::SizeT
fun crypto_sign_secretkeybytes() : LibC::SizeT fun crypto_sign_secretkeybytes() : LibC::SizeT
fun crypto_sign_bytes() : LibC::SizeT fun crypto_sign_bytes() : LibC::SizeT
fun crypto_kdf_keybytes() : LibC::SizeT
fun crypto_kdf_contextbytes() : LibC::SizeT
PUBLIC_KEY_BYTES = crypto_box_publickeybytes() PUBLIC_KEY_BYTES = crypto_box_publickeybytes()
SECRET_KEY_BYTES = crypto_box_secretkeybytes() SECRET_KEY_BYTES = crypto_box_secretkeybytes()
@ -18,6 +20,8 @@ module Cox
PUBLIC_SIGN_BYTES = crypto_sign_publickeybytes() PUBLIC_SIGN_BYTES = crypto_sign_publickeybytes()
SECRET_SIGN_BYTES = crypto_sign_secretkeybytes() SECRET_SIGN_BYTES = crypto_sign_secretkeybytes()
SIGNATURE_BYTES = crypto_sign_bytes() SIGNATURE_BYTES = crypto_sign_bytes()
KDF_KEY_BYTES = crypto_kdf_keybytes()
KDF_CONTEXT_BYTES = crypto_kdf_contextbytes()
fun crypto_box_keypair( fun crypto_box_keypair(
public_key_output : Pointer(LibC::UChar), public_key_output : Pointer(LibC::UChar),
@ -61,5 +65,14 @@ module Cox
message_size : LibC::ULongLong, message_size : LibC::ULongLong,
public_key : Pointer(LibC::UChar) public_key : Pointer(LibC::UChar)
) : LibC::Int ) : LibC::Int
fun crypto_kdf_derive_from_key(
subkey : Pointer(LibC::UChar),
subkey_len : LibC::SizeT,
subkey_id : UInt64,
ctx : Pointer(LibC::UChar),
key : Pointer(LibC::UChar)
) : LibC::Int
end end
end end