Partial internal switch to Crypto::Secret API

This commit is contained in:
Didactic Drunk 2021-06-16 14:50:26 -07:00
parent fca40d7764
commit 0e1b64b1bf
11 changed files with 153 additions and 112 deletions

View File

@ -7,15 +7,8 @@ require "../../../src/sodium/cipher/chalsa"
it "xors" do it "xors" do
data = Bytes.new(100) data = Bytes.new(100)
cipher1 = Sodium::Cipher::{{ name.id }}.new cipher1 = Sodium::Cipher::{{ name.id }}.random
cipher2 = Sodium::Cipher::{{ name.id }}.new cipher2 = Sodium::Cipher::{{ name.id }}.new key: cipher1.key, nonce: cipher1.nonce
key = cipher1.random_key
cipher2.key = key
nonce = cipher1.random_nonce
cipher2.nonce = nonce
output = cipher1.update data output = cipher1.update data
output.should_not eq data # Verify encryption did something. output.should_not eq data # Verify encryption did something.

View File

@ -1,9 +1,12 @@
require "../spec_helper" require "../spec_helper"
require "../../src/sodium/secure_buffer" require "../../src/sodium/secure_buffer"
require "crypto-secret/test"
class FakeError < Exception class FakeError < Exception
end end
test_secret_class Sodium::SecureBuffer
describe Sodium::SecureBuffer do describe Sodium::SecureBuffer do
it "allocates empty" do it "allocates empty" do
buf = Sodium::SecureBuffer.new 5 buf = Sodium::SecureBuffer.new 5
@ -18,13 +21,6 @@ describe Sodium::SecureBuffer do
buf.readwrite buf.readwrite
end end
it "allocates random" do
buf1 = Sodium::SecureBuffer.random 5
buf2 = Sodium::SecureBuffer.random 5
(buf1 == buf2).should be_false
buf1.wipe
end
it "copies and erases" do it "copies and erases" do
bytes = Bytes.new(5) { 1_u8 } bytes = Bytes.new(5) { 1_u8 }

View File

@ -20,40 +20,48 @@ detached_test_vectors = [
private def sign_from_vec(vec) private def sign_from_vec(vec)
seckey = Sodium::Sign::SecretKey.new seed: vec[:seed].hexbytes seckey = Sodium::Sign::SecretKey.new seed: vec[:seed].hexbytes
seckey.to_slice.should eq vec[:secret_key].hexbytes seckey.key.readonly do |sslice|
sslice.should eq vec[:secret_key].hexbytes
end
seckey.public_key.to_slice.should eq vec[:public_key].hexbytes seckey.public_key.to_slice.should eq vec[:public_key].hexbytes
plaintext = vec[:plaintext].hexbytes plaintext = vec[:plaintext].hexbytes
signature = vec[:signature].hexbytes signature = vec[:signature].hexbytes
{seckey, plaintext, signature} {seckey, plaintext, signature}
end end
private def new_sign_key_to_slice
Sodium::Sign::SecretKey.new.to_slice
end
describe Sodium::Sign::SecretKey do describe Sodium::Sign::SecretKey do
it "loads keys" do it "loads keys" do
key1 = Sodium::Sign::SecretKey.new key1 = Sodium::Sign::SecretKey.new
key2 = Sodium::Sign::SecretKey.new key1.to_slice, key1.public_key.to_slice key2 = key1.key.readonly do |kslice|
key1.to_slice.should eq key2.to_slice Sodium::Sign::SecretKey.new kslice, key1.public_key.to_slice
end
key1.key.should eq key2.key
key1.public_key.to_slice.should eq key2.public_key.to_slice key1.public_key.to_slice.should eq key2.public_key.to_slice
end end
it "recomputes the public key" do it "recomputes the public key" do
key1 = Sodium::Sign::SecretKey.new key1 = Sodium::Sign::SecretKey.new
key2 = Sodium::Sign::SecretKey.new key1.to_slice key2 = key1.key.readonly do |kslice|
key1.to_slice.should eq key2.to_slice Sodium::Sign::SecretKey.new kslice
end
key1.key.should eq key2.key
key1.public_key.to_slice.should eq key2.public_key.to_slice key1.public_key.to_slice.should eq key2.public_key.to_slice
end end
it "seed keys" do it "loading seed -> key -> seed" do
seed = Bytes.new Sodium::Sign::SecretKey::SEED_SIZE seed = Bytes.new Sodium::Sign::SecretKey::SEED_SIZE
key1 = Sodium::Sign::SecretKey.new seed: seed key1 = Sodium::Sign::SecretKey.new seed: seed
key2 = Sodium::Sign::SecretKey.new seed: Sodium::Sign::SecretKey.new(key1.to_slice).seed key2 = key1.key.readonly do |kslice|
key1.to_slice.should eq key2.to_slice Sodium::Sign::SecretKey.new kslice
end
key3 = Sodium::Sign::SecretKey.new seed: key2.seed
key1.key.should eq key2.key
key1.key.should eq key3.key
key1.public_key.to_slice.should eq key2.public_key.to_slice key1.public_key.to_slice.should eq key2.public_key.to_slice
key1.public_key.to_slice.should eq key3.public_key.to_slice
key1.seed.should eq seed key1.seed.should eq seed
key1.seed.should eq key2.seed key1.seed.should eq key2.seed
key1.seed.should eq key3.seed
end end
it "signs and verifies" do it "signs and verifies" do

View File

@ -12,7 +12,7 @@ module Sodium::Cipher::Aead
@key = SecureBuffer.random key_size @key = SecureBuffer.random key_size
end end
# Initializes with a reference to an existing ky. # Initializes with a reference to an existing key.
def initialize(@key : SecureBuffer) def initialize(@key : SecureBuffer)
raise ArgumentError.new("key size mismatch, got #{@key.bytesize}, wanted #{key_size}") if @key.bytesize != key_size raise ArgumentError.new("key size mismatch, got #{@key.bytesize}, wanted #{key_size}") if @key.bytesize != key_size
@key.readonly @key.readonly
@ -30,6 +30,7 @@ module Sodium::Cipher::Aead
offset = src.bytesize offset = src.bytesize
dst ||= Bytes.new (offset + mac_size) dst ||= Bytes.new (offset + mac_size)
mac = dst[offset, mac_size] mac = dst[offset, mac_size]
_, _, nonce = encrypt_detached src.to_slice, dst[0, offset], mac: mac, nonce: nonce, additional: additional _, _, nonce = encrypt_detached src.to_slice, dst[0, offset], mac: mac, nonce: nonce, additional: additional
{dst, nonce} {dst, nonce}
end end
@ -114,8 +115,8 @@ module Sodium::Cipher::Aead
ad_len = additional.try(&.bytesize) || 0 ad_len = additional.try(&.bytesize) || 0
nonce.used! nonce.used!
@key.readonly do @key.readonly do |kslice|
r = LibSodium.crypto_aead{{ val.id }}_encrypt_detached(dst, mac, out mac_len, src, src.bytesize, additional, ad_len, nil, nonce.to_slice, @key.to_slice) r = LibSodium.crypto_aead{{ val.id }}_encrypt_detached(dst, mac, out mac_len, src, src.bytesize, additional, ad_len, nil, nonce.to_slice, kslice)
raise Sodium::Error.new("crypto_aead_{{ val.id }}_encrypt_detached") if r != 0 raise Sodium::Error.new("crypto_aead_{{ val.id }}_encrypt_detached") if r != 0
raise Sodium::Error.new("crypto_aead_{{ val.id }}_encrypt_detached mac size mismatch") if mac_len != MAC_SIZE raise Sodium::Error.new("crypto_aead_{{ val.id }}_encrypt_detached mac size mismatch") if mac_len != MAC_SIZE
end end
@ -134,8 +135,9 @@ module Sodium::Cipher::Aead
ad_len = additional.try(&.bytesize) || 0 ad_len = additional.try(&.bytesize) || 0
r = @key.readonly do r = @key.readonly do |kslice|
LibSodium.crypto_aead{{ val.id }}_decrypt_detached(dst, nil, src, src.bytesize, mac, additional, ad_len, nonce.to_slice, @key.to_slice) LibSodium.crypto_aead{{ val.id }}_decrypt_detached(dst, nil, src, src.bytesize, mac, additional, ad_len, nonce.
to_slice, kslice)
end end
raise Sodium::Error::DecryptionFailed.new("crypto_aead_{{ val.id }}_decrypt_detached") if r != 0 raise Sodium::Error::DecryptionFailed.new("crypto_aead_{{ val.id }}_decrypt_detached") if r != 0
dst dst

View File

@ -6,27 +6,29 @@ module Sodium::Cipher
# #
# What? They're both dance? # What? They're both dance?
abstract class Chalsa abstract class Chalsa
@key : Bytes | SecureBuffer | Nil getter key : Crypto::Secret
@nonce : Bytes? getter! nonce : Bytes?
# Advanced usage. Don't touch. # Advanced usage. Don't touch.
property offset = 0 property offset = 0
getter! key def initialize(key : Crypto::Secret | Bytes, nonce = nil)
getter! nonce raise ArgumentError.new("key must be #{key_size} bytes, got #{key.bytesize}") if key.bytesize != key_size
@key = key.is_a?(Crypto::Secret) ? key : Sodium::SecureBuffer.new(key)
def initialize # self.key = key if key
end
def initialize(key = nil, nonce = nil)
self.key = key if key
self.nonce = nonce if nonce self.nonce = nonce if nonce
end end
def key=(key : Bytes | SecureBuffer) @[Deprecated("Use constructor to set key")]
def key=(key : Bytes | Crypto::Secret) : Crypto::Secret
raise ArgumentError.new("key must be #{key_size} bytes, got #{key.bytesize}") if key.bytesize != key_size raise ArgumentError.new("key must be #{key_size} bytes, got #{key.bytesize}") if key.bytesize != key_size
@key = key @key = case key
in Crypto::Secret
key key
in Bytes
Sodium::SecureBuffer.new key
end
@key.not_nil!
end end
def nonce=(nonce : Bytes) def nonce=(nonce : Bytes)
@ -35,6 +37,7 @@ module Sodium::Cipher
nonce nonce
end end
@[Deprecated("Use constructor to set key")]
def random_key def random_key
self.key = SecureBuffer.random key_size self.key = SecureBuffer.random key_size
end end
@ -103,6 +106,10 @@ module Sodium::Cipher
KEY_SIZE = LibSodium.crypto_stream_chacha20_{{ ietf ? "ietf_".id : "".id }}keybytes.to_i32 KEY_SIZE = LibSodium.crypto_stream_chacha20_{{ ietf ? "ietf_".id : "".id }}keybytes.to_i32
NONCE_SIZE = LibSodium.crypto_stream_chacha20_{{ ietf ? "ietf_".id : "".id }}noncebytes.to_i32 NONCE_SIZE = LibSodium.crypto_stream_chacha20_{{ ietf ? "ietf_".id : "".id }}noncebytes.to_i32
def self.random
new key: Sodium::SecureBuffer.random(KEY_SIZE), nonce: Random::Secure.random_bytes(NONCE_SIZE)
end
# Xor's src with the cipher output and places in dst. # Xor's src with the cipher output and places in dst.
# #
# src and dst may be the same object but should not overlap. # src and dst may be the same object but should not overlap.

View File

@ -3,7 +3,7 @@ require "../secure_buffer"
module Sodium::Cipher module Sodium::Cipher
abstract class SecretStream abstract class SecretStream
@state : SecureBuffer @state : Crypto::Secret
@encrypt_decrypt = 0 @encrypt_decrypt = 0
@initialized = false @initialized = false
@ -17,7 +17,7 @@ module Sodium::Cipher
# * This property is set to nil after calling .update. # * This property is set to nil after calling .update.
property additional : Bytes? = nil property additional : Bytes? = nil
@key : Bytes | SecureBuffer | Nil = nil @key : Crypto::Secret | Nil = nil
def initialize def initialize
@state = SecureBuffer.new state_size @state = SecureBuffer.new state_size
@ -31,15 +31,18 @@ module Sodium::Cipher
@encrypt_decrypt = -1 @encrypt_decrypt = -1
end end
def key=(key : Bytes | SecureBuffer) def key=(key : Bytes | Crypto::Secret) : Crypto::Secret
raise ArgumentError.new("key must be #{key_size} bytes, got #{key.bytesize}") if key.bytesize != key_size raise ArgumentError.new("key must be #{key_size} bytes, got #{key.bytesize}") if key.bytesize != key_size
key = key.is_a?(Crypto::Secret) ? key : Sodium::SecureBuffer.new(key)
@key = key @key = key
key key
end end
# Returns a random key in a SecureBuffer. # Returns a random key in a SecureBuffer.
def random_key def random_key : Crypto::Secret
self.key = SecureBuffer.random key_size k = SecureBuffer.random key_size
self.key = k
k
end end
# Only used for encryption. # Only used for encryption.
@ -102,16 +105,22 @@ module Sodium::Cipher
case @encrypt_decrypt case @encrypt_decrypt
when 1 when 1
if LibSodium.crypto_secretstream_{{ val.id }}_push(@state.to_slice, dst.to_slice, out dst_size, src, src.bytesize, ad, ad_size, @tag) != 0 dst_size = @state.readwrite do |stslice|
if LibSodium.crypto_secretstream_{{ val.id }}_push(stslice, dst.to_slice, out dsize, src, src.bytesize, ad, ad_size, @tag) != 0
raise Sodium::Error.new("crypto_streamsecret_{{ val.id }}_xor_ic") raise Sodium::Error.new("crypto_streamsecret_{{ val.id }}_xor_ic")
end end
dsize
end
@tag = 0 @tag = 0
@additional = nil @additional = nil
dst[0, dst_size] dst[0, dst_size]
when -1 when -1
if LibSodium.crypto_secretstream_{{ val.id }}_pull(@state.to_slice, dst.to_slice, out dst_size2, out @tag, src, src.bytesize, ad, ad_size) != 0 dst_size2 = @state.readwrite do |stslice|
if LibSodium.crypto_secretstream_{{ val.id }}_pull(stslice, dst.to_slice, out dsize2, out @tag, src, src.bytesize, ad, ad_size) != 0
raise Sodium::Error.new("crypto_streamsecret_{{ val.id }}_xor_ic") raise Sodium::Error.new("crypto_streamsecret_{{ val.id }}_xor_ic")
end end
dsize2
end
@additional = nil @additional = nil
dst[0, dst_size2] dst[0, dst_size2]
else else
@ -123,20 +132,26 @@ module Sodium::Cipher
raise Sodium::Error.new("can't initialize more than once") if @initialized raise Sodium::Error.new("can't initialize more than once") if @initialized
if k = @key if k = @key
k.readonly do |kslice|
case @encrypt_decrypt case @encrypt_decrypt
when 1 when 1
if LibSodium.crypto_secretstream_xchacha20poly1305_init_push(@state.to_slice, header_buf.to_slice, k.to_slice) != 0 @state.readwrite do |stslice|
if LibSodium.crypto_secretstream_xchacha20poly1305_init_push(stslice, header_buf.to_slice, kslice) != 0
raise Sodium::Error.new("crypto_secretstream_xchacha20poly1305_init_push") raise Sodium::Error.new("crypto_secretstream_xchacha20poly1305_init_push")
end end
end
when -1 when -1
if LibSodium.crypto_secretstream_xchacha20poly1305_init_pull(@state.to_slice, header_buf.to_slice, k.to_slice) != 0 @state.readwrite do |stslice|
if LibSodium.crypto_secretstream_xchacha20poly1305_init_pull(stslice, header_buf.to_slice, kslice) != 0
raise Sodium::Error.new("crypto_secretstream_xchacha20poly1305_init_push") raise Sodium::Error.new("crypto_secretstream_xchacha20poly1305_init_push")
end end
end
when 0 when 0
raise Sodium::Error.new("must call .encrypt or .decrypt first") raise Sodium::Error.new("must call .encrypt or .decrypt first")
else else
abort "invalid encrypt_decrypt state #{@encrypt_decrypt}" abort "invalid encrypt_decrypt state #{@encrypt_decrypt}"
end end
end
else else
raise Sodium::Error.new("must set an encryption/decryption key") raise Sodium::Error.new("must set an encryption/decryption key")
end end

View File

@ -38,6 +38,8 @@ module Sodium
# Returns key # Returns key
delegate_to_slice to: @sbuf delegate_to_slice to: @sbuf
@sbuf : Crypto::Secret
# Use an existing KDF key. # Use an existing KDF key.
# #
# * Copies key to a new SecureBuffer # * Copies key to a new SecureBuffer
@ -50,8 +52,8 @@ module Sodium
@sbuf = SecureBuffer.new(bytes, erase).noaccess @sbuf = SecureBuffer.new(bytes, erase).noaccess
end end
# Use an existing KDF SecureBuffer key. # Use an existing KDF Crypto::Secret key.
def initialize(@sbuf : SecureBuffer) def initialize(@sbuf : Crypto::Secret)
if @sbuf.bytesize != KEY_SIZE if @sbuf.bytesize != KEY_SIZE
raise ArgumentError.new("bytes must be #{KEY_SIZE}, got #{sbuf.bytesize}") raise ArgumentError.new("bytes must be #{KEY_SIZE}, got #{sbuf.bytesize}")
end end

View File

@ -25,7 +25,7 @@ module Sodium::Password
end end
# :nodoc: # :nodoc:
def derive_key(key : SecureBuffer, pass : Bytes | String, *, salt : Bytes? = nil) : Nil def derive_key(key : Crypto::Secret, pass : Bytes | String, *, salt : Bytes? = nil) : Nil
m = mode || raise ArgumentError.new("mode not set") m = mode || raise ArgumentError.new("mode not set")
salt ||= @salt salt ||= @salt
@ -33,10 +33,12 @@ module Sodium::Password
salt = salt.not_nil! salt = salt.not_nil!
raise "salt expected #{SALT_SIZE} bytes, got #{salt.bytesize} " if salt.bytesize != SALT_SIZE raise "salt expected #{SALT_SIZE} bytes, got #{salt.bytesize} " if salt.bytesize != SALT_SIZE
if LibSodium.crypto_pwhash(key.to_slice, key.bytesize, pass.to_slice, pass.bytesize, salt.to_slice, @ops, @mem, m) != 0 key.readwrite do |key_slice|
if LibSodium.crypto_pwhash(key_slice, key_slice.bytesize, pass.to_slice, pass.bytesize, salt.to_slice, @ops, @mem, m) != 0
raise Sodium::Error.new("crypto_pwhash") raise Sodium::Error.new("crypto_pwhash")
end end
end end
end
def to_params(*, salt = nil, key_size = nil, tcost : Float64? = nil) def to_params(*, salt = nil, key_size = nil, tcost : Float64? = nil)
Params.new @mode, @ops, @mem, salt: salt, key_size: key_size, tcost: tcost Params.new @mode, @ops, @mem, salt: salt, key_size: key_size, tcost: tcost

View File

@ -19,19 +19,19 @@ module Sodium
NONCE_SIZE = LibSodium.crypto_secretbox_noncebytes.to_i NONCE_SIZE = LibSodium.crypto_secretbox_noncebytes.to_i
MAC_SIZE = LibSodium.crypto_secretbox_macbytes.to_i MAC_SIZE = LibSodium.crypto_secretbox_macbytes.to_i
# Returns key @[Deprecated("Use `key.readonly` or `key.readwrite`")]
delegate_to_slice to: @key delegate_to_slice to: @key
# Encryption key # Encryption key
getter key : SecureBuffer getter key : Crypto::Secret
# Generate a new random key held in a SecureBuffer. # Generate a new random key held in a SecureBuffer.
def initialize def initialize
@key = SecureBuffer.random KEY_SIZE @key = SecureBuffer.random KEY_SIZE
end end
# Use an existing SecureBuffer. # Use an existing Crypto::Secret.
def initialize(@key : SecureBuffer) def initialize(@key : Crypto::Secret)
if @key.bytesize != KEY_SIZE if @key.bytesize != KEY_SIZE
raise ArgumentError.new("Secret key must be #{KEY_SIZE} bytes, got #{@key.bytesize}") raise ArgumentError.new("Secret key must be #{KEY_SIZE} bytes, got #{@key.bytesize}")
end end
@ -91,8 +91,8 @@ module Sodium
dst ||= Bytes.new dst_size dst ||= Bytes.new dst_size
raise ArgumentError.new("dst.bytesize must be src.bytesize - MAC_SIZE, got #{dst.bytesize}") if dst.bytesize != (src.bytesize - MAC_SIZE) raise ArgumentError.new("dst.bytesize must be src.bytesize - MAC_SIZE, got #{dst.bytesize}") if dst.bytesize != (src.bytesize - MAC_SIZE)
r = @key.readonly do r = @key.readonly do |kslice|
LibSodium.crypto_secretbox_open_easy(dst, src, src.bytesize, nonce.to_slice, @key) LibSodium.crypto_secretbox_open_easy(dst, src, src.bytesize, nonce.to_slice, kslice)
end end
raise Sodium::Error::DecryptionFailed.new("crypto_secretbox_easy") if r != 0 raise Sodium::Error::DecryptionFailed.new("crypto_secretbox_easy") if r != 0
dst dst

View File

@ -10,8 +10,6 @@ module Sodium
class SecureBuffer class SecureBuffer
include Crypto::Secret::Stateful include Crypto::Secret::Stateful
# @state = State::Readwrite
getter bytesize : Int32 getter bytesize : Int32
def initialize(@bytesize : Int32) def initialize(@bytesize : Int32)
@ -31,13 +29,13 @@ module Sodium
# :nodoc: # :nodoc:
# For .dup # For .dup
def initialize(sbuf : self) def initialize(sbuf : Crypto::Secret)
initialize sbuf.bytesize initialize sbuf.bytesize
# Maybe not thread safe # Maybe not thread safe
sbuf.readonly do |s1| sbuf.readonly do |sslice|
self.to_slice do |s2| readwrite do |dslice|
s1.copy_to s2.to_slice s1.copy_to s2
end end
end end
@ -63,8 +61,9 @@ module Sodium
Slice(UInt8).new @ptr, @bytesize Slice(UInt8).new @ptr, @bytesize
end end
def to_slice(& : Bytes -> Nil) protected def to_slice(& : Bytes -> Nil)
yield Bytes.new @ptr, @bytesize ro = @state < State::Readonly
yield Bytes.new(@ptr, @bytesize, read_only: ro)
end end
# :nodoc: # :nodoc:
@ -72,11 +71,6 @@ module Sodium
@ptr @ptr
end end
# WARNING: Not thread safe unless this object is readonly or readwrite
def dup
self.class.new self
end
protected def readwrite_impl : Nil protected def readwrite_impl : Nil
if LibSodium.sodium_mprotect_readwrite(@ptr) != 0 if LibSodium.sodium_mprotect_readwrite(@ptr) != 0
raise "sodium_mprotect_readwrite" raise "sodium_mprotect_readwrite"

View File

@ -20,17 +20,22 @@ module Sodium
getter public_key : PublicKey getter public_key : PublicKey
# Returns key @[Deprecated("Switching to Crypto::Secret. Use `key.readonly` or `key.readwrite`")]
delegate_to_slice to: @sbuf delegate_to_slice to: @key
getter key : Crypto::Secret
@seed : Crypto::Secret?
# Generates a new random secret/public key pair. # Generates a new random secret/public key pair.
def initialize def initialize
@sbuf = SecureBuffer.new KEY_SIZE @key = SecureBuffer.new KEY_SIZE
@public_key = PublicKey.new @public_key = PublicKey.new
if LibSodium.crypto_sign_keypair(@public_key.to_slice, self.to_slice) != 0 @key.readwrite do |kslice|
if LibSodium.crypto_sign_keypair(@public_key.to_slice, kslice) != 0
raise Sodium::Error.new("crypto_sign_keypair") raise Sodium::Error.new("crypto_sign_keypair")
end end
end end
end
# Use existing secret and public keys. # Use existing secret and public keys.
# Copies secret key to a SecureBuffer. # Copies secret key to a SecureBuffer.
@ -38,16 +43,18 @@ module Sodium
def initialize(bytes : Bytes, pkey : Bytes? = nil, *, erase = false) def initialize(bytes : Bytes, pkey : Bytes? = nil, *, erase = false)
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
@sbuf = SecureBuffer.new bytes, erase: erase @key = SecureBuffer.new bytes, erase: erase
if pk = pkey if pk = pkey
@public_key = PublicKey.new pk @public_key = PublicKey.new pk
else else
@public_key = PublicKey.new @public_key = PublicKey.new
if LibSodium.crypto_sign_ed25519_sk_to_pk(@public_key.to_slice, self.to_slice) != 0 @key.readwrite do |kslice|
if LibSodium.crypto_sign_ed25519_sk_to_pk(@public_key.to_slice, kslice) != 0
raise Sodium::Error.new("crypto_sign_ed25519_sk_to_pk") raise Sodium::Error.new("crypto_sign_ed25519_sk_to_pk")
end end
end 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.
# Copies seed to a SecureBuffer. # Copies seed to a SecureBuffer.
@ -61,18 +68,26 @@ 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
@sbuf = SecureBuffer.new KEY_SIZE @key = SecureBuffer.new KEY_SIZE
@public_key = PublicKey.new @public_key = PublicKey.new
if LibSodium.crypto_sign_seed_keypair(@public_key.to_slice, self.to_slice, seed.to_slice) != 0 seed.readonly do |seed_slice|
@key.readwrite do |kslice|
if LibSodium.crypto_sign_seed_keypair(@public_key.to_slice, kslice, seed_slice) != 0
raise Sodium::Error.new("crypto_sign_seed_keypair") raise Sodium::Error.new("crypto_sign_seed_keypair")
end end
end end
end
end
getter seed : SecureBuffer? do getter seed : Crypto::Secret? do
SecureBuffer.new(SEED_SIZE).tap do |s| SecureBuffer.new(SEED_SIZE).tap do |seed_buf|
if LibSodium.crypto_sign_ed25519_sk_to_seed(s.to_slice, self.to_slice) != 0 @key.readonly do |kslice|
seed_buf.readwrite do |seed_slice|
if LibSodium.crypto_sign_ed25519_sk_to_seed(seed_slice, kslice) != 0
raise Sodium::Error.new("crypto_sign_ed25519_sk_to_seed") raise Sodium::Error.new("crypto_sign_ed25519_sk_to_seed")
end end
end
end
end.readonly end.readonly
end end
@ -84,17 +99,24 @@ module Sodium
def sign_detached(message : Bytes) def sign_detached(message : Bytes)
sig = Bytes.new(SIG_SIZE) sig = Bytes.new(SIG_SIZE)
if LibSodium.crypto_sign_detached(sig, out sig_len, message, message.bytesize, self.to_slice) != 0 @key.readonly do |kslice|
if LibSodium.crypto_sign_detached(sig, out sig_len, message, message.bytesize, kslice) != 0
raise Error.new("crypto_sign_detached") raise Error.new("crypto_sign_detached")
end end
raise "expected #{sig.bytesize}, got #{sig_len}" if sig.bytesize != sig_len
end
sig sig
end end
def to_curve25519 : CryptoBox::SecretKey def to_curve25519 : CryptoBox::SecretKey
key = SecureBuffer.new CryptoBox::SecretKey::KEY_SIZE sbuf = SecureBuffer.new CryptoBox::SecretKey::KEY_SIZE
LibSodium.crypto_sign_ed25519_sk_to_curve25519 key.to_slice, @sbuf.to_slice sbuf.readwrite do |sbuf_slice|
key.readonly @key.readonly do |kslice|
CryptoBox::SecretKey.new key LibSodium.crypto_sign_ed25519_sk_to_curve25519 sbuf_slice, kslice
end
end
sbuf.readonly
CryptoBox::SecretKey.new sbuf
end end
end end
end end