From f036295aa3a518c88b13bf17608282c6319a7de6 Mon Sep 17 00:00:00 2001 From: Didactic Drunk <1479616+didactic-drunk@users.noreply.github.com> Date: Thu, 28 Apr 2022 12:14:35 -0700 Subject: [PATCH] Sign: Add `.copy_from` `.move_from` instead of *erase* Prefer `.random` Deprecate old `.initialize` --- README.md | 2 +- spec/sodium/sign/secret_key_spec.cr | 23 +++------- src/sodium/sign/secret_key.cr | 68 ++++++++++++++++++++--------- 3 files changed, 55 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 8fce8f1..8374b96 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Crystal bindings for the [libsodium API](https://libsodium.gitbook.io/doc/) - [Public-Key Cryptography](https://libsodium.gitbook.io/doc/public-key_cryptography) - [x] ☑ [Crypto Box Easy](https://libsodium.gitbook.io/doc/public-key_cryptography/authenticated_encryption) - [x] [Sealed Box](https://libsodium.gitbook.io/doc/public-key_cryptography/sealed_boxes) - - [ ] [Combined Signatures](https://libsodium.gitbook.io/doc/public-key_cryptography/public-key_signatures) + - [x] [Combined Signatures](https://libsodium.gitbook.io/doc/public-key_cryptography/public-key_signatures) - [x] ☑ [Detached Signatures](https://libsodium.gitbook.io/doc/public-key_cryptography/public-key_signatures) - [ ] [Pre-hashed Signatures](https://libsodium.gitbook.io/doc/public-key_cryptography/public-key_signatures) - [Secret-Key Cryptography](https://libsodium.gitbook.io/doc/secret-key_cryptography) diff --git a/spec/sodium/sign/secret_key_spec.cr b/spec/sodium/sign/secret_key_spec.cr index 1952bee..7150930 100644 --- a/spec/sodium/sign/secret_key_spec.cr +++ b/spec/sodium/sign/secret_key_spec.cr @@ -31,18 +31,9 @@ end describe Sodium::Sign::SecretKey do it "loads keys" do - key1 = Sodium::Sign::SecretKey.new + key1 = Sodium::Sign::SecretKey.random key2 = key1.key.readonly do |kslice| - 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 - end - - it "recomputes the public key" do - key1 = Sodium::Sign::SecretKey.new - key2 = key1.key.readonly do |kslice| - Sodium::Sign::SecretKey.new kslice + Sodium::Sign::SecretKey.copy_from kslice end key1.key.should eq key2.key key1.public_key.to_slice.should eq key2.public_key.to_slice @@ -52,7 +43,7 @@ describe Sodium::Sign::SecretKey do seed = Bytes.new Sodium::Sign::SecretKey::SEED_SIZE key1 = Sodium::Sign::SecretKey.new seed: seed key2 = key1.key.readonly do |kslice| - Sodium::Sign::SecretKey.new kslice + Sodium::Sign::SecretKey.copy_from kslice end key3 = Sodium::Sign::SecretKey.new seed: key2.seed key1.key.should eq key2.key @@ -66,7 +57,7 @@ describe Sodium::Sign::SecretKey do it "signs and verifies combined" do message = "foo" - skey = Sodium::Sign::SecretKey.new + skey = Sodium::Sign::SecretKey.random sig = skey.sign message message2 = skey.public_key.verify_string sig @@ -75,7 +66,7 @@ describe Sodium::Sign::SecretKey do it "signs and verifies detached" do message = "foo" - skey = Sodium::Sign::SecretKey.new + skey = Sodium::Sign::SecretKey.random sig = skey.sign_detached message skey.public_key.verify_detached message, sig @@ -83,7 +74,7 @@ describe Sodium::Sign::SecretKey do it "signs and fails" do message = "foo" - skey = Sodium::Sign::SecretKey.new + skey = Sodium::Sign::SecretKey.random sig = skey.sign_detached message expect_raises Sodium::Error::VerificationFailed do @@ -93,7 +84,7 @@ describe Sodium::Sign::SecretKey do it "to_curve25519" do message = "foo" - sskey = Sodium::Sign::SecretKey.new + sskey = Sodium::Sign::SecretKey.random cskey = sskey.to_curve25519 spkey = sskey.public_key diff --git a/src/sodium/sign/secret_key.cr b/src/sodium/sign/secret_key.cr index 523039d..c3ae296 100644 --- a/src/sodium/sign/secret_key.cr +++ b/src/sodium/sign/secret_key.cr @@ -18,12 +18,12 @@ module Sodium SIG_SIZE = LibSodium.crypto_sign_bytes.to_i SEED_SIZE = LibSodium.crypto_sign_seedbytes.to_i - getter public_key : PublicKey - @[Deprecated("Switching to Crypto::Secret. Use `key.readonly` or `key.readwrite`")] delegate_to_slice to: @key getter key : Crypto::Secret + getter public_key = PublicKey.new + @seed : Crypto::Secret? # Generates a new random secret/public key pair. @@ -37,40 +37,54 @@ module Sodium 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, *, erase = false) - raise ArgumentError.new("Secret sign key must be #{KEY_SIZE}, got #{bytes.bytesize}") unless bytes.bytesize == KEY_SIZE - @key = SecureBuffer.new bytes, erase: erase - if pk = pkey - @public_key = PublicKey.new pk + # Copies secret *key* to a `SecureBuffer` + def self.copy_from(key : Bytes) + new SecureBuffer.copy_from(key) + end + + # Copies secret *key* to a `SecureBuffer` + # Erases *key* + def self.move_from(key : Bytes) + new SecureBuffer.move_from(key) + end + + # Copies secret *key* to a `SecureBuffer` + @[Deprecated("Use .copy_from or .move_from")] + def self.new(key : Bytes, erase = false) + raise ArgumentError.new("Secret sign key must be #{KEY_SIZE}, got #{key.bytesize}") unless key.bytesize == KEY_SIZE + + if erase + move_from key else - @public_key = PublicKey.new - @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") - end + copy_from key + end + end + + # References existing Crypto::Secret + def initialize(@key : Crypto::Secret) + # Set @public_key + @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") 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) + # Copies seed to a `SecureBuffer` + def self.new(*, seed : Bytes, erase = false) raise ArgumentError.new("Secret sign seed must be #{SEED_SIZE}, got #{seed.bytesize}") unless seed.bytesize == SEED_SIZE - initialize(seed: SecureBuffer.new(seed, erase: erase)) + new(seed: SecureBuffer.new(seed, erase: erase)) end # Derive a new secret/public key pair based on a consistent seed. - # References passed SecureBuffer - def initialize(*, seed : SecureBuffer) + # References passed `SecureBuffer` + def initialize(*, seed : Crypto::Secret) raise ArgumentError.new("Secret sign seed must be #{SEED_SIZE}, got #{seed.bytesize}") unless seed.bytesize == SEED_SIZE @seed = seed @key = SecureBuffer.new KEY_SIZE - @public_key = PublicKey.new seed.readonly do |seed_slice| @key.readwrite do |kslice| if LibSodium.crypto_sign_seed_keypair(@public_key.to_slice, kslice, seed_slice) != 0 @@ -80,6 +94,18 @@ module Sodium end end + # :nodoc: + def initialize(random : Bool) + @key = SecureBuffer.new KEY_SIZE + @key.readwrite do |kslice| + if LibSodium.crypto_sign_keypair(@public_key.to_slice, kslice) != 0 + raise Sodium::Error.new("crypto_sign_keypair") + end + end + end + + + getter seed : Crypto::Secret do SecureBuffer.new(SEED_SIZE).tap do |seed_buf| @key.readonly do |kslice|