Add seed support to CryptoBox and Sign.

Wiping now supports multiple variables by Annotation.
This commit is contained in:
Didactic Drunk 2019-06-29 17:21:00 -07:00
parent 41a55a9593
commit 162cd72b0c
17 changed files with 149 additions and 66 deletions

View File

@ -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.

View File

@ -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

View File

@ -1,4 +1,4 @@
require "../spec_helper" require "../../spec_helper"
libsodium_comparisons = [ libsodium_comparisons = [
{ {

View File

@ -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

View File

@ -15,7 +15,3 @@ module Sodium
end end
require "./sodium/**" require "./sodium/**"
if Sodium::LibSodium.sodium_init == -1
abort "Failed to init libsodium"
end

View File

@ -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)

View File

@ -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}")

View File

@ -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

View File

@ -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")

View File

@ -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

View File

@ -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

View File

@ -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() NONCE_SIZE = crypto_box_noncebytes()
SECRET_KEY_SIZE = crypto_box_secretkeybytes() MAC_SIZE = crypto_box_macbytes()
NONCE_SIZE = crypto_box_noncebytes() SIGNATURE_SIZE = crypto_sign_bytes()
MAC_SIZE = crypto_box_macbytes()
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

View File

@ -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

View File

@ -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.

View File

@ -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}")

View File

@ -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
@public_key = PublicKey.new pkey
if pk = 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

View File

@ -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