Add seed support to CryptoBox and Sign.
Wiping now supports multiple variables by Annotation.
This commit is contained in:
parent
41a55a9593
commit
162cd72b0c
@ -6,8 +6,8 @@ Crystal bindings for the [libsodium API](https://libsodium.gitbook.io/doc/)
|
|||||||
|
|
||||||
## Goals
|
## Goals
|
||||||
|
|
||||||
* Provide an easy to use API based on reviewing most other [libsodium bindings](https://libsodium.gitbook.io/doc/bindings_for_other_languages).
|
|
||||||
* Provide the most commonly used libsodium API's.
|
* Provide the most commonly used libsodium API's.
|
||||||
|
* Provide an easy to use API based on reviewing most other [libsodium bindings](https://libsodium.gitbook.io/doc/bindings_for_other_languages).
|
||||||
* Test for compatibility against other libsodium bindings to ensure interoperability.
|
* Test for compatibility against other libsodium bindings to ensure interoperability.
|
||||||
* Always provide a stream interface to handle arbitrarily sized data when one is available.
|
* Always provide a stream interface to handle arbitrarily sized data when one is available.
|
||||||
* Drop in replacement classes compatible with OpenSSL::{Digest,Cipher} when possible.
|
* Drop in replacement classes compatible with OpenSSL::{Digest,Cipher} when possible.
|
||||||
|
@ -12,6 +12,13 @@ describe Sodium::CryptoBox::SecretKey do
|
|||||||
key1.public_key.bytes.should eq key2.public_key.bytes
|
key1.public_key.bytes.should eq key2.public_key.bytes
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "recomputes the public_key" do
|
||||||
|
key1 = Sodium::CryptoBox::SecretKey.new
|
||||||
|
key2 = Sodium::CryptoBox::SecretKey.new key1.bytes
|
||||||
|
key1.bytes.should eq key2.bytes
|
||||||
|
key1.public_key.bytes.should eq key2.public_key.bytes
|
||||||
|
end
|
||||||
|
|
||||||
it "seed keys" do
|
it "seed keys" do
|
||||||
seed = Bytes.new Sodium::CryptoBox::SecretKey::SEED_SIZE
|
seed = Bytes.new Sodium::CryptoBox::SecretKey::SEED_SIZE
|
||||||
key1 = Sodium::CryptoBox::SecretKey.new seed: seed
|
key1 = Sodium::CryptoBox::SecretKey.new seed: seed
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
require "../spec_helper"
|
require "../../spec_helper"
|
||||||
|
|
||||||
libsodium_comparisons = [
|
libsodium_comparisons = [
|
||||||
{
|
{
|
@ -1,7 +1,7 @@
|
|||||||
require "../../spec_helper"
|
require "../../spec_helper"
|
||||||
require "../../../src/sodium/sign/secret_key"
|
require "../../../src/sodium/sign/secret_key"
|
||||||
|
|
||||||
private def new_key_bytes
|
private def new_sign_key_bytes
|
||||||
Sodium::Sign::SecretKey.new.bytes
|
Sodium::Sign::SecretKey.new.bytes
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -11,8 +11,13 @@ describe Sodium::Sign::SecretKey do
|
|||||||
key2 = Sodium::Sign::SecretKey.new key1.bytes, key1.public_key.bytes
|
key2 = Sodium::Sign::SecretKey.new key1.bytes, key1.public_key.bytes
|
||||||
key1.bytes.should eq key2.bytes
|
key1.bytes.should eq key2.bytes
|
||||||
key1.public_key.bytes.should eq key2.public_key.bytes
|
key1.public_key.bytes.should eq key2.public_key.bytes
|
||||||
|
end
|
||||||
|
|
||||||
# TODO: test loading when missing public_key
|
it "recomputes the public key" do
|
||||||
|
key1 = Sodium::Sign::SecretKey.new
|
||||||
|
key2 = Sodium::Sign::SecretKey.new key1.bytes
|
||||||
|
key1.bytes.should eq key2.bytes
|
||||||
|
key1.public_key.bytes.should eq key2.public_key.bytes
|
||||||
end
|
end
|
||||||
|
|
||||||
it "seed keys" do
|
it "seed keys" do
|
||||||
@ -42,6 +47,6 @@ describe Sodium::Sign::SecretKey do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "checks wiped" do
|
it "checks wiped" do
|
||||||
check_wiped new_key_bytes
|
check_wiped new_sign_key_bytes
|
||||||
end
|
end
|
||||||
end
|
end
|
@ -15,7 +15,3 @@ module Sodium
|
|||||||
end
|
end
|
||||||
|
|
||||||
require "./sodium/**"
|
require "./sodium/**"
|
||||||
|
|
||||||
if Sodium::LibSodium.sodium_init == -1
|
|
||||||
abort "Failed to init libsodium"
|
|
||||||
end
|
|
||||||
|
@ -5,6 +5,7 @@ module Sodium::CryptoBox
|
|||||||
include Wipe
|
include Wipe
|
||||||
|
|
||||||
# BUG: precompute size
|
# BUG: precompute size
|
||||||
|
@[Wipe::Var]
|
||||||
@bytes = Bytes.new(1)
|
@bytes = Bytes.new(1)
|
||||||
|
|
||||||
def initialize(@secret_key : SecretKey, @public_key : PublicKey)
|
def initialize(@secret_key : SecretKey, @public_key : PublicKey)
|
||||||
|
@ -7,6 +7,12 @@ module Sodium::CryptoBox
|
|||||||
|
|
||||||
getter bytes : Bytes
|
getter bytes : Bytes
|
||||||
|
|
||||||
|
# :nodoc:
|
||||||
|
# Only used by SecretKey
|
||||||
|
def initialize
|
||||||
|
@bytes = Bytes.new KEY_SIZE
|
||||||
|
end
|
||||||
|
|
||||||
def initialize(@bytes : Bytes)
|
def initialize(@bytes : Bytes)
|
||||||
if bytes.bytesize != KEY_SIZE
|
if bytes.bytesize != KEY_SIZE
|
||||||
raise ArgumentError.new("Public key must be #{KEY_SIZE} bytes, got #{bytes.bytesize}")
|
raise ArgumentError.new("Public key must be #{KEY_SIZE} bytes, got #{bytes.bytesize}")
|
||||||
|
@ -1,34 +1,42 @@
|
|||||||
require "../lib_sodium"
|
require "../lib_sodium"
|
||||||
|
|
||||||
module Sodium::CryptoBox
|
module Sodium::CryptoBox
|
||||||
|
# WARNING: This class takes ownership of any key material passed to it.
|
||||||
|
# If you don't want this behavior pass a duplicate of the key/seed to initialize().
|
||||||
class SecretKey < Key
|
class SecretKey < Key
|
||||||
include Wipe
|
include Wipe
|
||||||
KEY_SIZE = LibSodium.crypto_box_secretkeybytes
|
KEY_SIZE = LibSodium.crypto_box_secretkeybytes
|
||||||
SEED_SIZE = LibSodium.crypto_box_seedbytes
|
SEED_SIZE = LibSodium.crypto_box_seedbytes
|
||||||
MAC_SIZE = LibSodium::MAC_SIZE
|
MAC_SIZE = LibSodium::MAC_SIZE
|
||||||
|
|
||||||
getter public_key
|
getter public_key : PublicKey
|
||||||
|
|
||||||
|
@[Wipe::Var]
|
||||||
getter bytes : Bytes
|
getter bytes : Bytes
|
||||||
|
@[Wipe::Var]
|
||||||
@seed : Bytes?
|
@seed : Bytes?
|
||||||
|
|
||||||
# Generate a new random secret/public key pair.
|
# Generate a new random secret/public key pair.
|
||||||
def initialize
|
def initialize
|
||||||
pkey = Bytes.new(PublicKey::KEY_SIZE)
|
|
||||||
@bytes = Bytes.new(KEY_SIZE)
|
@bytes = Bytes.new(KEY_SIZE)
|
||||||
@public_key = PublicKey.new pkey
|
@public_key = PublicKey.new
|
||||||
v = LibSodium.crypto_box_keypair(pkey, @bytes)
|
if LibSodium.crypto_box_keypair(@public_key.bytes, @bytes) != 0
|
||||||
if v != 0
|
raise Sodium::Error.new("crypto_box_keypair")
|
||||||
raise Sodium::Error.new("crypto_box_keypair #{v}")
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Use existing Secret and Public keys.
|
# Use existing secret and public keys.
|
||||||
def initialize(@bytes : Bytes, pkey : Bytes)
|
# Recomputes the public key from a secret key if missing.
|
||||||
# TODO: finish regenerating public_key
|
def initialize(@bytes : Bytes, pkey : Bytes? = nil)
|
||||||
if bytes.bytesize != KEY_SIZE
|
raise ArgumentError.new("Secret key must be #{KEY_SIZE} bytes, got #{bytes.bytesize}") if bytes.bytesize != KEY_SIZE
|
||||||
raise ArgumentError.new("Secret key must be #{KEY_SIZE} bytes, got #{bytes.bytesize}")
|
if pk = pkey
|
||||||
|
@public_key = PublicKey.new pk
|
||||||
|
else
|
||||||
|
@public_key = PublicKey.new
|
||||||
|
if LibSodium.crypto_scalarmult_base(@public_key.bytes, @bytes) != 0
|
||||||
|
raise Sodium::Error.new("crypto_scalarmult_base")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
@public_key = PublicKey.new pkey
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Derive a new secret/public key pair based on a consistent seed.
|
# Derive a new secret/public key pair based on a consistent seed.
|
||||||
@ -36,10 +44,9 @@ module Sodium::CryptoBox
|
|||||||
raise ArgumentError.new("Secret sign seed must be #{SEED_SIZE}, got #{seed.bytesize}") unless seed.bytesize == SEED_SIZE
|
raise ArgumentError.new("Secret sign seed must be #{SEED_SIZE}, got #{seed.bytesize}") unless seed.bytesize == SEED_SIZE
|
||||||
@seed = seed
|
@seed = seed
|
||||||
|
|
||||||
pkey = Bytes.new(PublicKey::KEY_SIZE)
|
|
||||||
@bytes = Bytes.new(KEY_SIZE)
|
@bytes = Bytes.new(KEY_SIZE)
|
||||||
@public_key = PublicKey.new pkey
|
@public_key = PublicKey.new
|
||||||
if LibSodium.crypto_box_seed_keypair(pkey, @bytes, seed) != 0
|
if LibSodium.crypto_box_seed_keypair(@public_key.bytes, @bytes, seed) != 0
|
||||||
raise Sodium::Error.new("crypto_box_seed_keypair")
|
raise Sodium::Error.new("crypto_box_seed_keypair")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -51,11 +58,11 @@ module Sodium::CryptoBox
|
|||||||
|
|
||||||
# Create a new box and automatically close when the block exits.
|
# Create a new box and automatically close when the block exits.
|
||||||
def box(public_key)
|
def box(public_key)
|
||||||
pa = box public_key
|
b = box public_key
|
||||||
begin
|
begin
|
||||||
yield pa
|
yield b
|
||||||
ensure
|
ensure
|
||||||
pa.close
|
b.close
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -4,6 +4,7 @@ module Sodium::Digest
|
|||||||
class Blake2b
|
class Blake2b
|
||||||
# provides copying digest/hexdigest methods
|
# provides copying digest/hexdigest methods
|
||||||
include OpenSSL::DigestBase
|
include OpenSSL::DigestBase
|
||||||
|
include Wipe
|
||||||
|
|
||||||
KEY_SIZE = LibSodium.crypto_generichash_blake2b_keybytes
|
KEY_SIZE = LibSodium.crypto_generichash_blake2b_keybytes
|
||||||
KEY_SIZE_MIN = LibSodium.crypto_generichash_blake2b_keybytes_min
|
KEY_SIZE_MIN = LibSodium.crypto_generichash_blake2b_keybytes_min
|
||||||
@ -19,42 +20,40 @@ module Sodium::Digest
|
|||||||
|
|
||||||
getter digest_size
|
getter digest_size
|
||||||
|
|
||||||
|
@[Wipe::Var]
|
||||||
@state = StaticArray(UInt8, 384).new 0
|
@state = StaticArray(UInt8, 384).new 0
|
||||||
@key_size = 0
|
@key_size = 0
|
||||||
@have_salt = false
|
|
||||||
@have_personal = false
|
|
||||||
|
|
||||||
# implemented as static array's so clone works without jumping through hoops.
|
# implemented as static array's so clone works without jumping through hoops.
|
||||||
|
@[Wipe::Var]
|
||||||
@key = StaticArray(UInt8, 64).new 0
|
@key = StaticArray(UInt8, 64).new 0
|
||||||
@salt = StaticArray(UInt8, 16).new 0
|
@salt = StaticArray(UInt8, 16).new 0
|
||||||
@personal = StaticArray(UInt8, 16).new 0
|
@personal = StaticArray(UInt8, 16).new 0
|
||||||
|
|
||||||
def initialize(@digest_size : Int32 = OUT_SIZE, key : Bytes? = nil, salt : Bytes? = nil, personal : Bytes? = nil)
|
def initialize(@digest_size : Int32 = OUT_SIZE, key : Bytes? = nil, salt : Bytes? = nil, personal : Bytes? = nil)
|
||||||
if k = key
|
if k = key
|
||||||
raise ArgumentError.new("key larger than KEY_SIZE_MAX, got #{k.bytesize}") if k.bytesize > KEY_SIZE_MAX
|
raise ArgumentError.new("key larger than KEY_SIZE_MAX(#{KEY_SIZE_MAX}), got #{k.bytesize}") if k.bytesize > KEY_SIZE_MAX
|
||||||
@key_size = k.bytesize
|
@key_size = k.bytesize
|
||||||
k.copy_to @key.to_slice
|
k.copy_to @key.to_slice
|
||||||
end
|
end
|
||||||
|
|
||||||
if sa = salt
|
if sa = salt
|
||||||
raise ArgumentError.new("salt must be SALT_SIZE bytes, got #{sa.bytesize}") if sa.bytesize != SALT_SIZE
|
raise ArgumentError.new("salt must be SALT_SIZE(#{SALT_SIZE}) bytes, got #{sa.bytesize}") if sa.bytesize != SALT_SIZE
|
||||||
sa.copy_to @salt.to_slice
|
sa.copy_to @salt.to_slice
|
||||||
@have_salt = true
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if pe = personal
|
if pe = personal
|
||||||
raise ArgumentError.new("personal must be PERSONAL_SIZE bytes, got #{pe.bytesize}") if pe.bytesize != PERSONAL_SIZE
|
raise ArgumentError.new("personal must be PERSONAL_SIZE(#{PERSONAL_SIZE}) bytes, got #{pe.bytesize}") if pe.bytesize != PERSONAL_SIZE
|
||||||
pe.copy_to @personal.to_slice
|
pe.copy_to @personal.to_slice
|
||||||
@have_personal = true
|
|
||||||
end
|
end
|
||||||
|
|
||||||
reset
|
reset
|
||||||
end
|
end
|
||||||
|
|
||||||
def reset
|
def reset
|
||||||
key = @key_size > 0 ? @key.to_unsafe : nil
|
key = @key.to_unsafe
|
||||||
salt = @have_salt ? @salt.to_unsafe : nil
|
salt = @salt.to_unsafe
|
||||||
personal = @have_personal ? @personal.to_unsafe : nil
|
personal = @personal.to_unsafe
|
||||||
|
|
||||||
if LibSodium.crypto_generichash_blake2b_init_salt_personal(@state, key, @key_size, @digest_size, salt, personal) != 0
|
if LibSodium.crypto_generichash_blake2b_init_salt_personal(@state, key, @key_size, @digest_size, salt, personal) != 0
|
||||||
raise Sodium::Error.new("blake2b_init_key_salt_personal")
|
raise Sodium::Error.new("blake2b_init_key_salt_personal")
|
||||||
|
@ -1,26 +1,29 @@
|
|||||||
module Sodium
|
module Sodium
|
||||||
class Kdf
|
class Kdf
|
||||||
|
KDF_KEY_SIZE = LibSodium.crypto_kdf_keybytes
|
||||||
|
KDF_CONTEXT_SIZE = LibSodium.crypto_kdf_contextbytes
|
||||||
|
|
||||||
property bytes : Bytes
|
property bytes : Bytes
|
||||||
|
|
||||||
delegate to_slice, to: @bytes
|
delegate to_slice, to: @bytes
|
||||||
|
|
||||||
def initialize(bytes : Bytes)
|
def initialize(bytes : Bytes)
|
||||||
if bytes.bytesize != LibSodium::KDF_KEY_SIZE
|
if bytes.bytesize != KDF_KEY_SIZE
|
||||||
raise ArgumentError.new("bytes must be #{LibSodium::KDF_KEY_SIZE}, got #{bytes.bytesize}")
|
raise ArgumentError.new("bytes must be #{KDF_KEY_SIZE}, got #{bytes.bytesize}")
|
||||||
end
|
end
|
||||||
|
|
||||||
@bytes = bytes
|
@bytes = bytes
|
||||||
end
|
end
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
@bytes = Random::Secure.random_bytes(LibSodium::KDF_KEY_SIZE)
|
@bytes = Random::Secure.random_bytes(KDF_KEY_SIZE)
|
||||||
end
|
end
|
||||||
|
|
||||||
# context must be 8 bytes
|
# context must be 8 bytes
|
||||||
# subkey_size must be 16..64 bytes as of libsodium 1.0.17
|
# subkey_size must be 16..64 bytes as of libsodium 1.0.17
|
||||||
def derive(context, subkey_id, subkey_size)
|
def derive(context, subkey_id, subkey_size)
|
||||||
if context.bytesize != LibSodium::KDF_CONTEXT_SIZE
|
if context.bytesize != KDF_CONTEXT_SIZE
|
||||||
raise ArgumentError.new("context must be #{LibSodium::KDF_CONTEXT_SIZE}, got #{context.bytesize}")
|
raise ArgumentError.new("context must be #{KDF_CONTEXT_SIZE}, got #{context.bytesize}")
|
||||||
end
|
end
|
||||||
|
|
||||||
subkey = Bytes.new subkey_size
|
subkey = Bytes.new subkey_size
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
|
require "./wipe"
|
||||||
|
|
||||||
module Sodium
|
module Sodium
|
||||||
abstract class Key
|
abstract class Key
|
||||||
|
include Sodium::Wipe
|
||||||
|
|
||||||
abstract def bytes
|
abstract def bytes
|
||||||
|
|
||||||
delegate to_slice, to: @bytes
|
delegate to_slice, to: @bytes
|
||||||
|
@ -42,14 +42,9 @@ module Sodium
|
|||||||
fun crypto_generichash_blake2b_personalbytes : LibC::SizeT
|
fun crypto_generichash_blake2b_personalbytes : LibC::SizeT
|
||||||
fun sodium_memzero(Pointer(LibC::UChar), LibC::SizeT) : Nil
|
fun sodium_memzero(Pointer(LibC::UChar), LibC::SizeT) : Nil
|
||||||
|
|
||||||
PUBLIC_KEY_SIZE = crypto_box_publickeybytes()
|
|
||||||
SECRET_KEY_SIZE = crypto_box_secretkeybytes()
|
|
||||||
NONCE_SIZE = crypto_box_noncebytes()
|
NONCE_SIZE = crypto_box_noncebytes()
|
||||||
MAC_SIZE = crypto_box_macbytes()
|
MAC_SIZE = crypto_box_macbytes()
|
||||||
SIGNATURE_SIZE = crypto_sign_bytes()
|
SIGNATURE_SIZE = crypto_sign_bytes()
|
||||||
KDF_KEY_SIZE = crypto_kdf_keybytes()
|
|
||||||
KDF_CONTEXT_SIZE = crypto_kdf_contextbytes()
|
|
||||||
PWHASH_STR_SIZE = crypto_pwhash_strbytes()
|
|
||||||
|
|
||||||
fun crypto_secretbox_easy(
|
fun crypto_secretbox_easy(
|
||||||
output : Pointer(LibC::UChar),
|
output : Pointer(LibC::UChar),
|
||||||
@ -93,6 +88,11 @@ module Sodium
|
|||||||
seed : Pointer(LibC::UChar)
|
seed : Pointer(LibC::UChar)
|
||||||
) : LibC::Int
|
) : LibC::Int
|
||||||
|
|
||||||
|
fun crypto_scalarmult_base(
|
||||||
|
public_key_output : Pointer(LibC::UChar),
|
||||||
|
secret_key_output : Pointer(LibC::UChar)
|
||||||
|
) : LibC::Int
|
||||||
|
|
||||||
fun crypto_box_easy(
|
fun crypto_box_easy(
|
||||||
output : Pointer(LibC::UChar),
|
output : Pointer(LibC::UChar),
|
||||||
data : Pointer(LibC::UChar),
|
data : Pointer(LibC::UChar),
|
||||||
@ -111,6 +111,21 @@ module Sodium
|
|||||||
recipient_secret_key : Pointer(LibC::UChar)
|
recipient_secret_key : Pointer(LibC::UChar)
|
||||||
) : LibC::Int
|
) : LibC::Int
|
||||||
|
|
||||||
|
fun crypto_box_seal(
|
||||||
|
output : Pointer(LibC::UChar),
|
||||||
|
data : Pointer(LibC::UChar),
|
||||||
|
data_size : LibC::ULongLong,
|
||||||
|
recipient_public_key : Pointer(LibC::UChar)
|
||||||
|
) : LibC::Int
|
||||||
|
|
||||||
|
fun crypto_box_seal_open(
|
||||||
|
output : Pointer(LibC::UChar),
|
||||||
|
data : Pointer(LibC::UChar),
|
||||||
|
data_size : LibC::ULongLong,
|
||||||
|
recipient_public_key : Pointer(LibC::UChar),
|
||||||
|
recipient_secret_key : Pointer(LibC::UChar)
|
||||||
|
) : LibC::Int
|
||||||
|
|
||||||
fun crypto_sign_keypair(
|
fun crypto_sign_keypair(
|
||||||
public_key_output : Pointer(LibC::UChar),
|
public_key_output : Pointer(LibC::UChar),
|
||||||
secret_key_output : Pointer(LibC::UChar)
|
secret_key_output : Pointer(LibC::UChar)
|
||||||
@ -122,6 +137,11 @@ module Sodium
|
|||||||
seed : Pointer(LibC::UChar)
|
seed : Pointer(LibC::UChar)
|
||||||
) : LibC::Int
|
) : LibC::Int
|
||||||
|
|
||||||
|
fun crypto_sign_ed25519_sk_to_pk(
|
||||||
|
public_key_output : Pointer(LibC::UChar),
|
||||||
|
secret_key_output : Pointer(LibC::UChar)
|
||||||
|
) : LibC::Int
|
||||||
|
|
||||||
fun crypto_sign_detached(
|
fun crypto_sign_detached(
|
||||||
signature_output : Pointer(LibC::UChar),
|
signature_output : Pointer(LibC::UChar),
|
||||||
signature_output_size : Pointer(LibC::ULongLong),
|
signature_output_size : Pointer(LibC::ULongLong),
|
||||||
@ -198,6 +218,10 @@ module Sodium
|
|||||||
) : LibC::Int
|
) : LibC::Int
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if LibSodium.sodium_init != 0
|
||||||
|
abort "Failed to init libsodium"
|
||||||
|
end
|
||||||
|
|
||||||
if LibSodium.crypto_secretbox_noncebytes != LibSodium.crypto_box_noncebytes
|
if LibSodium.crypto_secretbox_noncebytes != LibSodium.crypto_box_noncebytes
|
||||||
raise "Assumptions in this library regarding nonce sizes may not be valid"
|
raise "Assumptions in this library regarding nonce sizes may not be valid"
|
||||||
end
|
end
|
||||||
|
@ -18,6 +18,8 @@ module Sodium
|
|||||||
MEMLIMIT_MAX = LibSodium.crypto_pwhash_memlimit_max
|
MEMLIMIT_MAX = LibSodium.crypto_pwhash_memlimit_max
|
||||||
MEMLIMIT_INTERACTIVE = LibSodium.crypto_pwhash_memlimit_interactive
|
MEMLIMIT_INTERACTIVE = LibSodium.crypto_pwhash_memlimit_interactive
|
||||||
|
|
||||||
|
PWHASH_STR_SIZE = LibSodium.crypto_pwhash_strbytes
|
||||||
|
|
||||||
# Use the most recent algorithm Argon2id13 for new applications.
|
# Use the most recent algorithm Argon2id13 for new applications.
|
||||||
enum Algorithm
|
enum Algorithm
|
||||||
Argon2i13 = 1
|
Argon2i13 = 1
|
||||||
@ -37,7 +39,7 @@ module Sodium
|
|||||||
# * the automatically generated salt used for the previous computation
|
# * the automatically generated salt used for the previous computation
|
||||||
# * the other parameters required to verify the password, including the algorithm identifier, its version, opslimit and memlimit.
|
# * the other parameters required to verify the password, including the algorithm identifier, its version, opslimit and memlimit.
|
||||||
def store(pass)
|
def store(pass)
|
||||||
outstr = Bytes.new LibSodium::PWHASH_STR_SIZE
|
outstr = Bytes.new PWHASH_STR_SIZE
|
||||||
if LibSodium.crypto_pwhash_str(outstr, pass, pass.bytesize, @opslimit, @memlimit) != 0
|
if LibSodium.crypto_pwhash_str(outstr, pass, pass.bytesize, @opslimit, @memlimit) != 0
|
||||||
raise Sodium::Error.new("crypto_pwhash_str")
|
raise Sodium::Error.new("crypto_pwhash_str")
|
||||||
end
|
end
|
||||||
|
@ -17,6 +17,7 @@ module Sodium
|
|||||||
NONCE_SIZE = LibSodium.crypto_secretbox_noncebytes
|
NONCE_SIZE = LibSodium.crypto_secretbox_noncebytes
|
||||||
MAC_SIZE = LibSodium::MAC_SIZE
|
MAC_SIZE = LibSodium::MAC_SIZE
|
||||||
|
|
||||||
|
@[Wipe::Var]
|
||||||
property bytes : Bytes
|
property bytes : Bytes
|
||||||
|
|
||||||
# Generate a new random key.
|
# Generate a new random key.
|
||||||
|
@ -7,6 +7,12 @@ module Sodium
|
|||||||
|
|
||||||
getter bytes : Bytes
|
getter bytes : Bytes
|
||||||
|
|
||||||
|
# :nodoc:
|
||||||
|
# Only used by SecretKey
|
||||||
|
def initialize
|
||||||
|
@bytes = Bytes.new(KEY_SIZE)
|
||||||
|
end
|
||||||
|
|
||||||
def initialize(@bytes : Bytes)
|
def initialize(@bytes : Bytes)
|
||||||
if bytes.bytesize != KEY_SIZE
|
if bytes.bytesize != KEY_SIZE
|
||||||
raise ArgumentError.new("Public key must be #{KEY_SIZE} bytes, got #{bytes.bytesize}")
|
raise ArgumentError.new("Public key must be #{KEY_SIZE} bytes, got #{bytes.bytesize}")
|
||||||
|
@ -8,32 +8,38 @@ module Sodium
|
|||||||
# key.public_key.verify_detached data
|
# key.public_key.verify_detached data
|
||||||
# ```
|
# ```
|
||||||
class Sign::SecretKey < Sodium::Key
|
class Sign::SecretKey < Sodium::Key
|
||||||
include Wipe
|
|
||||||
KEY_SIZE = LibSodium.crypto_sign_secretkeybytes
|
KEY_SIZE = LibSodium.crypto_sign_secretkeybytes
|
||||||
SEED_SIZE = LibSodium.crypto_sign_seedbytes
|
SEED_SIZE = LibSodium.crypto_sign_seedbytes
|
||||||
|
|
||||||
|
getter public_key : PublicKey
|
||||||
|
|
||||||
|
@[Wipe::Var]
|
||||||
getter bytes : Bytes
|
getter bytes : Bytes
|
||||||
getter public_key
|
@[Wipe::Var]
|
||||||
@seed : Bytes?
|
@seed : Bytes?
|
||||||
|
|
||||||
# Generates a new random secret/public key pair.
|
# Generates a new random secret/public key pair.
|
||||||
def initialize
|
def initialize
|
||||||
pkey = Bytes.new(Sign::PublicKey::KEY_SIZE)
|
|
||||||
@bytes = Bytes.new(KEY_SIZE)
|
@bytes = Bytes.new(KEY_SIZE)
|
||||||
@public_key = PublicKey.new pkey
|
@public_key = PublicKey.new
|
||||||
if LibSodium.crypto_sign_keypair(pkey, @bytes) != 0
|
if LibSodium.crypto_sign_keypair(@public_key.bytes, @bytes) != 0
|
||||||
raise Sodium::Error.new("crypto_sign_keypair")
|
raise Sodium::Error.new("crypto_sign_keypair")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Use existing Secret and Public keys.
|
# Use existing secret and public keys.
|
||||||
|
# Recomputes the public key from a secret key if missing.
|
||||||
def initialize(@bytes : Bytes, pkey : Bytes? = nil)
|
def initialize(@bytes : Bytes, pkey : Bytes? = nil)
|
||||||
pkey ||= Bytes.new(Sign::PublicKey::KEY_SIZE).tap do |pk|
|
|
||||||
# BUG: Finish regenerating public_key
|
|
||||||
raise "Needs crypto_sign_ed25519_sk_to_pk"
|
|
||||||
end
|
|
||||||
raise ArgumentError.new("Secret sign key must be #{KEY_SIZE}, got #{@bytes.bytesize}") unless @bytes.bytesize == KEY_SIZE
|
raise ArgumentError.new("Secret sign key must be #{KEY_SIZE}, got #{@bytes.bytesize}") unless @bytes.bytesize == KEY_SIZE
|
||||||
|
|
||||||
|
if pk = pkey
|
||||||
@public_key = PublicKey.new pkey
|
@public_key = PublicKey.new pkey
|
||||||
|
else
|
||||||
|
@public_key = PublicKey.new
|
||||||
|
if LibSodium.crypto_sign_ed25519_sk_to_pk(@public_key.bytes, @bytes) != 0
|
||||||
|
raise Sodium::Error.new("crypto_sign_ed25519_sk_to_pk")
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Derive a new secret/public key pair based on a consistent seed.
|
# Derive a new secret/public key pair based on a consistent seed.
|
||||||
@ -41,10 +47,9 @@ module Sodium
|
|||||||
raise ArgumentError.new("Secret sign seed must be #{SEED_SIZE}, got #{seed.bytesize}") unless seed.bytesize == SEED_SIZE
|
raise ArgumentError.new("Secret sign seed must be #{SEED_SIZE}, got #{seed.bytesize}") unless seed.bytesize == SEED_SIZE
|
||||||
@seed = seed
|
@seed = seed
|
||||||
|
|
||||||
pkey = Bytes.new(Sign::PublicKey::KEY_SIZE)
|
|
||||||
@bytes = Bytes.new(KEY_SIZE)
|
@bytes = Bytes.new(KEY_SIZE)
|
||||||
@public_key = PublicKey.new pkey
|
@public_key = PublicKey.new
|
||||||
if LibSodium.crypto_sign_seed_keypair(pkey, @bytes, seed) != 0
|
if LibSodium.crypto_sign_seed_keypair(@public_key.bytes, @bytes, seed) != 0
|
||||||
raise Sodium::Error.new("crypto_sign_seed_keypair")
|
raise Sodium::Error.new("crypto_sign_seed_keypair")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
module Sodium::Wipe
|
module Sodium::Wipe
|
||||||
|
annotation Var
|
||||||
|
end
|
||||||
|
|
||||||
@closed = false
|
@closed = false
|
||||||
|
|
||||||
def close
|
def close
|
||||||
@ -9,7 +12,21 @@ module Sodium::Wipe
|
|||||||
|
|
||||||
protected def wipe
|
protected def wipe
|
||||||
return if @closed
|
return if @closed
|
||||||
Sodium.memzero @bytes
|
|
||||||
|
{% for ivar in @type.instance_vars %}
|
||||||
|
{% if ann = ivar.annotation(Wipe::Var) %}
|
||||||
|
{% if ivar.type.id == StaticArray.id %}
|
||||||
|
#puts "wiping static {{ivar}}"
|
||||||
|
# Sodium.memzero @{{ ivar.id }}.to_slice
|
||||||
|
{% else %}
|
||||||
|
if var = @{{ ivar.id }}
|
||||||
|
#puts "wiping {{ivar}}"
|
||||||
|
# Sodium.memzero var
|
||||||
|
Sodium.memzero var.to_slice
|
||||||
|
end
|
||||||
|
{% end %}
|
||||||
|
{% end %}
|
||||||
|
{% end %}
|
||||||
end
|
end
|
||||||
|
|
||||||
def finalize
|
def finalize
|
||||||
|
Loading…
Reference in New Issue
Block a user