Sodum::CryptoBox::SecretKey may derive keys from a seed.
Sodum::Sign::SecretKey may derive keys from a seed. Sodum::CryptoBox::Pair renamed to Sodum::CryptoBox::Box
This commit is contained in:
parent
2f4d9ddb6b
commit
41a55a9593
@ -144,14 +144,14 @@ alice = Sodium::CryptoBox::SecretKey.new
|
|||||||
bob = Sodium::CryptoBox::SecretKey.new
|
bob = Sodium::CryptoBox::SecretKey.new
|
||||||
|
|
||||||
# Precompute a shared secret between alice and bob.
|
# Precompute a shared secret between alice and bob.
|
||||||
pair = alice.pair bob.public_key
|
box = alice.box bob.public_key
|
||||||
|
|
||||||
# Encrypt a message for Bob using his public key, signing it with Alice's
|
# Encrypt a message for Bob using his public key, signing it with Alice's
|
||||||
# secret key
|
# secret key
|
||||||
nonce, encrypted = pair.encrypt data
|
nonce, encrypted = box.encrypt data
|
||||||
|
|
||||||
# Precompute within a block. The shared secret is wiped when the block exits.
|
# Precompute within a block. The shared secret is wiped when the block exits.
|
||||||
bob.pair alice.public_key do |pair|
|
bob.box alice.public_key do |box|
|
||||||
# Decrypt the message using Bob's secret key, and verify its signature against
|
# Decrypt the message using Bob's secret key, and verify its signature against
|
||||||
# Alice's public key
|
# Alice's public key
|
||||||
decrypted = Sodium.decrypt(encrypted, nonce, alice.public, bob.secret)
|
decrypted = Sodium.decrypt(encrypted, nonce, alice.public, bob.secret)
|
||||||
|
@ -1,6 +1,25 @@
|
|||||||
require "../../spec_helper"
|
require "../../spec_helper"
|
||||||
|
|
||||||
|
private def new_key_bytes
|
||||||
|
Sodium::CryptoBox::SecretKey.new.bytes
|
||||||
|
end
|
||||||
|
|
||||||
describe Sodium::CryptoBox::SecretKey do
|
describe Sodium::CryptoBox::SecretKey do
|
||||||
|
it "loads keys" do
|
||||||
|
key1 = Sodium::CryptoBox::SecretKey.new
|
||||||
|
key2 = Sodium::CryptoBox::SecretKey.new key1.bytes, key1.public_key.bytes
|
||||||
|
key1.bytes.should eq key2.bytes
|
||||||
|
key1.public_key.bytes.should eq key2.public_key.bytes
|
||||||
|
end
|
||||||
|
|
||||||
|
it "seed keys" do
|
||||||
|
seed = Bytes.new Sodium::CryptoBox::SecretKey::SEED_SIZE
|
||||||
|
key1 = Sodium::CryptoBox::SecretKey.new seed: seed
|
||||||
|
key2 = Sodium::CryptoBox::SecretKey.new seed: seed
|
||||||
|
key1.bytes.should eq key2.bytes
|
||||||
|
key1.public_key.bytes.should eq key2.public_key.bytes
|
||||||
|
end
|
||||||
|
|
||||||
it "easy encrypt/decrypt" do
|
it "easy encrypt/decrypt" do
|
||||||
data = "Hello World!"
|
data = "Hello World!"
|
||||||
|
|
||||||
@ -12,15 +31,19 @@ describe Sodium::CryptoBox::SecretKey do
|
|||||||
|
|
||||||
# Encrypt a message for Bob using his public key, signing it with Alice's
|
# Encrypt a message for Bob using his public key, signing it with Alice's
|
||||||
# secret key
|
# secret key
|
||||||
pair = alice.pair bob.public_key
|
box = alice.box bob.public_key
|
||||||
nonce, encrypted = pair.encrypt_easy data
|
nonce, encrypted = box.encrypt_easy data
|
||||||
|
|
||||||
# Decrypt the message using Bob's secret key, and verify its signature against
|
# Decrypt the message using Bob's secret key, and verify its signature against
|
||||||
# Alice's public key
|
# Alice's public key
|
||||||
bob.pair alice.public_key do |pair|
|
bob.box alice.public_key do |box|
|
||||||
decrypted = pair.decrypt_easy encrypted, nonce: nonce
|
decrypted = box.decrypt_easy encrypted, nonce: nonce
|
||||||
|
|
||||||
String.new(decrypted).should eq(data)
|
String.new(decrypted).should eq(data)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "wipes keys" do
|
||||||
|
check_wiped new_key_bytes
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,7 +1,28 @@
|
|||||||
require "../../spec_helper"
|
require "../../spec_helper"
|
||||||
require "../../../src/sodium/sign/secret_key"
|
require "../../../src/sodium/sign/secret_key"
|
||||||
|
|
||||||
|
private def new_key_bytes
|
||||||
|
Sodium::Sign::SecretKey.new.bytes
|
||||||
|
end
|
||||||
|
|
||||||
describe Sodium::Sign::SecretKey do
|
describe Sodium::Sign::SecretKey do
|
||||||
|
it "loads keys" do
|
||||||
|
key1 = Sodium::Sign::SecretKey.new
|
||||||
|
key2 = Sodium::Sign::SecretKey.new key1.bytes, key1.public_key.bytes
|
||||||
|
key1.bytes.should eq key2.bytes
|
||||||
|
key1.public_key.bytes.should eq key2.public_key.bytes
|
||||||
|
|
||||||
|
# TODO: test loading when missing public_key
|
||||||
|
end
|
||||||
|
|
||||||
|
it "seed keys" do
|
||||||
|
seed = Bytes.new Sodium::Sign::SecretKey::SEED_SIZE
|
||||||
|
key1 = Sodium::Sign::SecretKey.new seed: seed
|
||||||
|
key2 = Sodium::Sign::SecretKey.new seed: seed
|
||||||
|
key1.bytes.should eq key2.bytes
|
||||||
|
key1.public_key.bytes.should eq key2.public_key.bytes
|
||||||
|
end
|
||||||
|
|
||||||
it "signs and verifies" do
|
it "signs and verifies" do
|
||||||
message = "foo"
|
message = "foo"
|
||||||
skey = Sodium::Sign::SecretKey.new
|
skey = Sodium::Sign::SecretKey.new
|
||||||
@ -19,4 +40,8 @@ describe Sodium::Sign::SecretKey do
|
|||||||
skey.public_key.verify_detached "bar", sig
|
skey.public_key.verify_detached "bar", sig
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "checks wiped" do
|
||||||
|
check_wiped new_key_bytes
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,2 +1,9 @@
|
|||||||
require "spec"
|
require "spec"
|
||||||
require "../src/sodium"
|
require "../src/sodium"
|
||||||
|
|
||||||
|
def check_wiped(buf : Bytes)
|
||||||
|
GC.collect
|
||||||
|
buf.each do |b|
|
||||||
|
raise "not wiped #{buf.inspect}" if b != 0_u8
|
||||||
|
end
|
||||||
|
end
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
require "../lib_sodium"
|
require "../lib_sodium"
|
||||||
|
|
||||||
module Sodium::CryptoBox
|
module Sodium::CryptoBox
|
||||||
class Pair
|
class Box
|
||||||
include Wipe
|
include Wipe
|
||||||
|
|
||||||
# BUG: precompute size
|
# BUG: precompute size
|
@ -3,7 +3,7 @@ require "../lib_sodium"
|
|||||||
module Sodium::CryptoBox
|
module Sodium::CryptoBox
|
||||||
class PublicKey < Key
|
class PublicKey < Key
|
||||||
include Wipe
|
include Wipe
|
||||||
KEY_SIZE = LibSodium::PUBLIC_KEY_SIZE
|
KEY_SIZE = LibSodium.crypto_box_publickeybytes
|
||||||
|
|
||||||
getter bytes : Bytes
|
getter bytes : Bytes
|
||||||
|
|
||||||
|
@ -3,36 +3,55 @@ require "../lib_sodium"
|
|||||||
module Sodium::CryptoBox
|
module Sodium::CryptoBox
|
||||||
class SecretKey < Key
|
class SecretKey < Key
|
||||||
include Wipe
|
include Wipe
|
||||||
KEY_SIZE = LibSodium::SECRET_KEY_SIZE
|
KEY_SIZE = LibSodium.crypto_box_secretkeybytes
|
||||||
MAC_SIZE = LibSodium::MAC_SIZE
|
SEED_SIZE = LibSodium.crypto_box_seedbytes
|
||||||
|
MAC_SIZE = LibSodium::MAC_SIZE
|
||||||
|
|
||||||
getter public_key
|
getter public_key
|
||||||
getter bytes : Bytes
|
getter bytes : Bytes
|
||||||
|
@seed : Bytes?
|
||||||
|
|
||||||
# Generate a new secret/public key pair.
|
# Generate a new random secret/public key pair.
|
||||||
def initialize
|
def initialize
|
||||||
pkey = Bytes.new(PublicKey::KEY_SIZE)
|
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 pkey
|
||||||
LibSodium.crypto_box_keypair(pkey, @bytes)
|
v = LibSodium.crypto_box_keypair(pkey, @bytes)
|
||||||
|
if v != 0
|
||||||
|
raise Sodium::Error.new("crypto_box_keypair #{v}")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Use existing Secret and Public keys.
|
# Use existing Secret and Public keys.
|
||||||
def initialize(@bytes : Bytes, pkey : Bytes)
|
def initialize(@bytes : Bytes, pkey : Bytes)
|
||||||
|
# TODO: finish regenerating public_key
|
||||||
if bytes.bytesize != KEY_SIZE
|
if bytes.bytesize != KEY_SIZE
|
||||||
raise ArgumentError.new("Secret key must be #{KEY_SIZE} bytes, got #{bytes.bytesize}")
|
raise ArgumentError.new("Secret key must be #{KEY_SIZE} bytes, got #{bytes.bytesize}")
|
||||||
end
|
end
|
||||||
@public_key = PublicKey.new pkey
|
@public_key = PublicKey.new pkey
|
||||||
end
|
end
|
||||||
|
|
||||||
# Return a Pair containing a precomputed shared secret for use with encryption/decryption.
|
# Derive a new secret/public key pair based on a consistent seed.
|
||||||
def pair(public_key) : Pair
|
def initialize(*, seed : Bytes)
|
||||||
Pair.new self, public_key
|
raise ArgumentError.new("Secret sign seed must be #{SEED_SIZE}, got #{seed.bytesize}") unless seed.bytesize == SEED_SIZE
|
||||||
|
@seed = seed
|
||||||
|
|
||||||
|
pkey = Bytes.new(PublicKey::KEY_SIZE)
|
||||||
|
@bytes = Bytes.new(KEY_SIZE)
|
||||||
|
@public_key = PublicKey.new pkey
|
||||||
|
if LibSodium.crypto_box_seed_keypair(pkey, @bytes, seed) != 0
|
||||||
|
raise Sodium::Error.new("crypto_box_seed_keypair")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Create a new pair and automatically close when the block exits.
|
# Return a Box containing a precomputed shared secret for use with encryption/decryption.
|
||||||
def pair(public_key)
|
def box(public_key) : Box
|
||||||
pa = pair public_key
|
Box.new self, public_key
|
||||||
|
end
|
||||||
|
|
||||||
|
# Create a new box and automatically close when the block exits.
|
||||||
|
def box(public_key)
|
||||||
|
pa = box public_key
|
||||||
begin
|
begin
|
||||||
yield pa
|
yield pa
|
||||||
ensure
|
ensure
|
||||||
|
@ -5,11 +5,16 @@ module Sodium
|
|||||||
|
|
||||||
fun crypto_box_publickeybytes : LibC::SizeT
|
fun crypto_box_publickeybytes : LibC::SizeT
|
||||||
fun crypto_box_secretkeybytes : LibC::SizeT
|
fun crypto_box_secretkeybytes : LibC::SizeT
|
||||||
|
fun crypto_box_seedbytes : LibC::SizeT
|
||||||
fun crypto_box_noncebytes : LibC::SizeT
|
fun crypto_box_noncebytes : LibC::SizeT
|
||||||
fun crypto_box_macbytes : LibC::SizeT
|
fun crypto_box_macbytes : LibC::SizeT
|
||||||
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_sign_seedbytes : LibC::SizeT
|
||||||
|
fun crypto_secretbox_keybytes : LibC::SizeT
|
||||||
|
fun crypto_secretbox_noncebytes : LibC::SizeT
|
||||||
|
fun crypto_secretbox_macbytes : LibC::SizeT
|
||||||
fun crypto_kdf_keybytes : LibC::SizeT
|
fun crypto_kdf_keybytes : LibC::SizeT
|
||||||
fun crypto_kdf_contextbytes : LibC::SizeT
|
fun crypto_kdf_contextbytes : LibC::SizeT
|
||||||
fun crypto_pwhash_memlimit_min : LibC::SizeT
|
fun crypto_pwhash_memlimit_min : LibC::SizeT
|
||||||
@ -41,8 +46,6 @@ module Sodium
|
|||||||
SECRET_KEY_SIZE = crypto_box_secretkeybytes()
|
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()
|
||||||
PUBLIC_SIGN_SIZE = crypto_sign_publickeybytes()
|
|
||||||
SECRET_SIGN_SIZE = crypto_sign_secretkeybytes()
|
|
||||||
SIGNATURE_SIZE = crypto_sign_bytes()
|
SIGNATURE_SIZE = crypto_sign_bytes()
|
||||||
KDF_KEY_SIZE = crypto_kdf_keybytes()
|
KDF_KEY_SIZE = crypto_kdf_keybytes()
|
||||||
KDF_CONTEXT_SIZE = crypto_kdf_contextbytes()
|
KDF_CONTEXT_SIZE = crypto_kdf_contextbytes()
|
||||||
@ -82,7 +85,13 @@ module Sodium
|
|||||||
fun crypto_box_keypair(
|
fun crypto_box_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)
|
||||||
)
|
) : LibC::Int
|
||||||
|
|
||||||
|
fun crypto_box_seed_keypair(
|
||||||
|
public_key_output : Pointer(LibC::UChar),
|
||||||
|
secret_key_output : Pointer(LibC::UChar),
|
||||||
|
seed : Pointer(LibC::UChar)
|
||||||
|
) : LibC::Int
|
||||||
|
|
||||||
fun crypto_box_easy(
|
fun crypto_box_easy(
|
||||||
output : Pointer(LibC::UChar),
|
output : Pointer(LibC::UChar),
|
||||||
@ -107,6 +116,12 @@ module Sodium
|
|||||||
secret_key_output : Pointer(LibC::UChar)
|
secret_key_output : Pointer(LibC::UChar)
|
||||||
) : LibC::Int
|
) : LibC::Int
|
||||||
|
|
||||||
|
fun crypto_sign_seed_keypair(
|
||||||
|
public_key_output : Pointer(LibC::UChar),
|
||||||
|
secret_key_output : Pointer(LibC::UChar),
|
||||||
|
seed : 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),
|
||||||
@ -182,4 +197,12 @@ module Sodium
|
|||||||
output_len : UInt64
|
output_len : UInt64
|
||||||
) : LibC::Int
|
) : LibC::Int
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if LibSodium.crypto_secretbox_noncebytes != LibSodium.crypto_box_noncebytes
|
||||||
|
raise "Assumptions in this library regarding nonce sizes may not be valid"
|
||||||
|
end
|
||||||
|
|
||||||
|
if LibSodium.crypto_secretbox_macbytes != LibSodium.crypto_box_macbytes
|
||||||
|
raise "Assumptions in this library regarding mac sizes may not be valid"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -13,8 +13,9 @@ module Sodium
|
|||||||
# message = key.decrypt_easy encrypted, nonce
|
# message = key.decrypt_easy encrypted, nonce
|
||||||
# ```
|
# ```
|
||||||
class SecretBox < Key
|
class SecretBox < Key
|
||||||
KEY_SIZE = LibSodium::SECRET_KEY_SIZE
|
KEY_SIZE = LibSodium.crypto_secretbox_keybytes
|
||||||
MAC_SIZE = LibSodium::MAC_SIZE
|
NONCE_SIZE = LibSodium.crypto_secretbox_noncebytes
|
||||||
|
MAC_SIZE = LibSodium::MAC_SIZE
|
||||||
|
|
||||||
property bytes : Bytes
|
property bytes : Bytes
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ require "../lib_sodium"
|
|||||||
module Sodium
|
module Sodium
|
||||||
class Sign::PublicKey < Key
|
class Sign::PublicKey < Key
|
||||||
include Wipe
|
include Wipe
|
||||||
KEY_SIZE = LibSodium::PUBLIC_SIGN_SIZE
|
KEY_SIZE = LibSodium.crypto_sign_publickeybytes
|
||||||
|
|
||||||
getter bytes : Bytes
|
getter bytes : Bytes
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ module Sodium
|
|||||||
end
|
end
|
||||||
|
|
||||||
def verify_detached(message : Bytes, sig : Bytes)
|
def verify_detached(message : Bytes, sig : Bytes)
|
||||||
raise ArgumentError.new("Signature must be #{LibSodium::SIGNATURE_SIZE} bytes, got #{sig.bytesize}")
|
raise ArgumentError.new("Signature must be #{LibSodium::SIGNATURE_SIZE} bytes, got #{sig.bytesize}") if sig.bytesize != LibSodium::SIGNATURE_SIZE
|
||||||
|
|
||||||
v = LibSodium.crypto_sign_verify_detached sig, message, message.bytesize, @bytes
|
v = LibSodium.crypto_sign_verify_detached sig, message, message.bytesize, @bytes
|
||||||
if v != 0
|
if v != 0
|
||||||
|
@ -1,36 +1,53 @@
|
|||||||
require "../lib_sodium"
|
require "../lib_sodium"
|
||||||
|
|
||||||
module Sodium
|
module Sodium
|
||||||
|
# Usage:
|
||||||
|
# ```
|
||||||
|
# key = SecretKey.new
|
||||||
|
# sig = key.sign_detached data
|
||||||
|
# key.public_key.verify_detached data
|
||||||
|
# ```
|
||||||
class Sign::SecretKey < Sodium::Key
|
class Sign::SecretKey < Sodium::Key
|
||||||
include Wipe
|
include Wipe
|
||||||
KEY_SIZE = LibSodium::SECRET_SIGN_SIZE
|
KEY_SIZE = LibSodium.crypto_sign_secretkeybytes
|
||||||
|
SEED_SIZE = LibSodium.crypto_sign_seedbytes
|
||||||
|
|
||||||
getter bytes : Bytes
|
getter bytes : Bytes
|
||||||
getter public_key
|
getter public_key
|
||||||
|
@seed : Bytes?
|
||||||
|
|
||||||
# Generates a new secret/public key pair.
|
# Generates a new random secret/public key pair.
|
||||||
def initialize
|
def initialize
|
||||||
pkey = Bytes.new(Sign::PublicKey::KEY_SIZE)
|
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 pkey
|
||||||
LibSodium.crypto_sign_keypair pkey, @bytes
|
if LibSodium.crypto_sign_keypair(pkey, @bytes) != 0
|
||||||
|
raise Sodium::Error.new("crypto_sign_keypair")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Use existing Secret and Public keys.
|
# Use existing Secret and Public keys.
|
||||||
def initialize(@bytes : Bytes, pkey : Bytes)
|
def initialize(@bytes : Bytes, pkey : Bytes? = nil)
|
||||||
raise ArgumentError.new("Secret sign key must be #{KEY_SIZE}, got #{@bytes.bytesize}")
|
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
|
||||||
@public_key = PublicKey.new pkey
|
@public_key = PublicKey.new pkey
|
||||||
end
|
end
|
||||||
|
|
||||||
# def initialize(@bytes : Bytes)
|
# Derive a new secret/public key pair based on a consistent seed.
|
||||||
# if bytes.bytesize != KEY_SIZE
|
def initialize(*, seed : Bytes)
|
||||||
# raise ArgumentError.new("Secret key must be #{KEY_SIZE} bytes, got #{bytes.bytesize}")
|
raise ArgumentError.new("Secret sign seed must be #{SEED_SIZE}, got #{seed.bytesize}") unless seed.bytesize == SEED_SIZE
|
||||||
# end
|
@seed = seed
|
||||||
# BUG: fix
|
|
||||||
# @public_key = PublicKey.new Bytes.new(100)
|
pkey = Bytes.new(Sign::PublicKey::KEY_SIZE)
|
||||||
# raise "Needs crypto_sign_ed25519_sk_to_pk"
|
@bytes = Bytes.new(KEY_SIZE)
|
||||||
# Also needs to differentiate from seed as a single parameter
|
@public_key = PublicKey.new pkey
|
||||||
# end
|
if LibSodium.crypto_sign_seed_keypair(pkey, @bytes, seed) != 0
|
||||||
|
raise Sodium::Error.new("crypto_sign_seed_keypair")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Signs message and returns a detached signature.
|
# Signs message and returns a detached signature.
|
||||||
# Verify using `secret_key.public_key.verify_detached(message, sig)`
|
# Verify using `secret_key.public_key.verify_detached(message, sig)`
|
||||||
|
Loading…
Reference in New Issue
Block a user