sodium.cr/src/sodium/crypto_box/secret_key.cr
Didactic Drunk d1c8829fcf API changed all Key classes .bytes to .to_slice
Switched most custom Wipe implementation to libsodium guarded memory.
2019-07-03 18:04:13 -07:00

104 lines
3.5 KiB
Crystal

require "../lib_sodium"
require "../key"
require "./public_key"
require "../crypto_box"
class Sodium::CryptoBox
# Key used for encryption + authentication or encryption without authentication, not for unencrypted signing.
#
# If you don't want this behavior pass a duplicate of the key/seed to initialize().
class SecretKey < Key
KEY_SIZE = LibSodium.crypto_box_secretkeybytes.to_i
SEED_SIZE = LibSodium.crypto_box_seedbytes.to_i
SEAL_SIZE = LibSodium.crypto_box_sealbytes.to_i
getter public_key : PublicKey
delegate to_slice, to: @sbuf
@seed : SecureBuffer?
# Generate a new random secret/public key pair.
def initialize
@sbuf = SecureBuffer.new KEY_SIZE
@public_key = PublicKey.new
if LibSodium.crypto_box_keypair(@public_key.to_slice, self.to_slice) != 0
raise Sodium::Error.new("crypto_box_keypair")
end
end
# Use existing secret and public keys.
# Copies secret key to a SecureBuffer.
# Recomputes the public key from a secret key if missing.
def initialize(bytes : Bytes, pkey : Bytes? = nil)
raise ArgumentError.new("Secret key must be #{KEY_SIZE} bytes, got #{bytes.bytesize}") if bytes.bytesize != KEY_SIZE
@sbuf = SecureBuffer.new bytes
if pk = pkey
@public_key = PublicKey.new pk
else
@public_key = PublicKey.new
if LibSodium.crypto_scalarmult_base(@public_key.to_slice, self.to_slice) != 0
raise Sodium::Error.new("crypto_scalarmult_base")
end
end
end
# Derive a new secret/public key pair based on a consistent seed.
# Copies seed to a SecureBuffer.
def initialize(*, seed : Bytes, erase = false)
raise ArgumentError.new("Secret sign seed must be #{SEED_SIZE}, got #{seed.bytesize}") unless seed.bytesize == SEED_SIZE
@seed = SecureBuffer.new seed, erase: erase
@sbuf = SecureBuffer.new KEY_SIZE
@public_key = PublicKey.new
if LibSodium.crypto_box_seed_keypair(@public_key.to_slice, self.to_slice, seed) != 0
raise Sodium::Error.new("crypto_box_seed_keypair")
end
end
# Derive a new secret/public key pair based on a consistent seed.
def initialize(*, seed : SecureBuffer)
raise ArgumentError.new("Secret sign seed must be #{SEED_SIZE}, got #{seed.bytesize}") unless seed.bytesize == SEED_SIZE
@seed = seed
@sbuf = SecureBuffer.new KEY_SIZE
@public_key = PublicKey.new
if LibSodium.crypto_box_seed_keypair(@public_key.to_slice, self.to_slice, seed) != 0
raise Sodium::Error.new("crypto_box_seed_keypair")
end
end
def seed
# BUG: Generate seed if not set.
@seed.not_nil!.to_slice
end
# Return a Box containing a precomputed shared secret for use with authenticated encryption/decryption.
def box(public_key) : CryptoBox
CryptoBox.new self, public_key
end
# Create a new box and automatically close when the block exits.
def box(public_key)
b = box public_key
begin
yield b
ensure
b.close
end
end
# Anonymously receive messages without a signatures.
# For authenticated messages use `secret_key.box(recipient_public_key).decrypt`.
def decrypt(src)
encrypt src.to_slice
end
def decrypt(src : Bytes, dst : Bytes = Bytes.new(src.bytesize - SEAL_SIZE)) : Bytes
if LibSodium.crypto_box_seal_open(dst, src, src.bytesize, @public_key.to_slice, self.to_slice) != 0
raise Sodium::Error.new("crypto_box_seal_open")
end
dst
end
end
end