From 58dddebeb81dc6fab945d2b10a91588eddc471c2 Mon Sep 17 00:00:00 2001 From: Adam Cooke Date: Wed, 13 Mar 2024 16:54:29 +0000 Subject: [PATCH] fix: fixes `postal default-dkim-record` --- app/lib/signer.rb | 66 ---------------------------- lib/postal/config.rb | 1 + lib/postal/signer.rb | 67 ++++++++++++++++++++++++++++ spec/lib/postal/signer_spec.rb | 79 ++++++++++++++++++++++++++++++++++ spec/lib/postal_spec.rb | 2 +- spec/lib/signer_spec.rb | 76 -------------------------------- 6 files changed, 148 insertions(+), 143 deletions(-) delete mode 100644 app/lib/signer.rb create mode 100644 lib/postal/signer.rb create mode 100644 spec/lib/postal/signer_spec.rb delete mode 100644 spec/lib/signer_spec.rb diff --git a/app/lib/signer.rb b/app/lib/signer.rb deleted file mode 100644 index 4234a653..00000000 --- a/app/lib/signer.rb +++ /dev/null @@ -1,66 +0,0 @@ -# frozen_string_literal: true - -require "base64" - -class Signer - - # Create a new Signer - # - # @param [OpenSSL::PKey::RSA] private_key The private key to use for signing - # @return [Signer] - def initialize(private_key) - @private_key = private_key - end - - # Return the private key - # - # @return [OpenSSL::PKey::RSA] - attr_reader :private_key - - # Return the public key for the private key - # - # @return [OpenSSL::PKey::RSA] - def public_key - @private_key.public_key - end - - # Sign the given data - # - # @param [String] data The data to sign - # @return [String] The signature - def sign(data) - private_key.sign(OpenSSL::Digest.new("SHA256"), data) - end - - # Sign the given data and return a Base64-encoded signature - # - # @param [String] data The data to sign - # @return [String] The Base64-encoded signature - def sign64(data) - Base64.strict_encode64(sign(data)) - end - - # Return a JWK for the private key - # - # @return [JWT::JWK] The JWK - def jwk - @jwk ||= JWT::JWK.new(private_key, { use: "sig", alg: "RS256" }) - end - - # Sign the given data using SHA1 (for legacy use) - # - # @param [String] data The data to sign - # @return [String] The signature - def sha1_sign(data) - private_key.sign(OpenSSL::Digest.new("SHA1"), data) - end - - # Sign the given data using SHA1 (for legacy use) and return a Base64-encoded string - # - # @param [String] data The data to sign - # @return [String] The signature - def sha1_sign64(data) - Base64.strict_encode64(sha1_sign(data)) - end - -end diff --git a/lib/postal/config.rb b/lib/postal/config.rb index 40b6b20e..627ea7e3 100644 --- a/lib/postal/config.rb +++ b/lib/postal/config.rb @@ -16,6 +16,7 @@ require_relative "version" require_relative "config_schema" require_relative "legacy_config_source" +require_relative "signer" module Postal diff --git a/lib/postal/signer.rb b/lib/postal/signer.rb new file mode 100644 index 00000000..a6007768 --- /dev/null +++ b/lib/postal/signer.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +require "base64" +module Postal + class Signer + + # Create a new Signer + # + # @param [OpenSSL::PKey::RSA] private_key The private key to use for signing + # @return [Signer] + def initialize(private_key) + @private_key = private_key + end + + # Return the private key + # + # @return [OpenSSL::PKey::RSA] + attr_reader :private_key + + # Return the public key for the private key + # + # @return [OpenSSL::PKey::RSA] + def public_key + @private_key.public_key + end + + # Sign the given data + # + # @param [String] data The data to sign + # @return [String] The signature + def sign(data) + private_key.sign(OpenSSL::Digest.new("SHA256"), data) + end + + # Sign the given data and return a Base64-encoded signature + # + # @param [String] data The data to sign + # @return [String] The Base64-encoded signature + def sign64(data) + Base64.strict_encode64(sign(data)) + end + + # Return a JWK for the private key + # + # @return [JWT::JWK] The JWK + def jwk + @jwk ||= JWT::JWK.new(private_key, { use: "sig", alg: "RS256" }) + end + + # Sign the given data using SHA1 (for legacy use) + # + # @param [String] data The data to sign + # @return [String] The signature + def sha1_sign(data) + private_key.sign(OpenSSL::Digest.new("SHA1"), data) + end + + # Sign the given data using SHA1 (for legacy use) and return a Base64-encoded string + # + # @param [String] data The data to sign + # @return [String] The signature + def sha1_sign64(data) + Base64.strict_encode64(sha1_sign(data)) + end + + end +end diff --git a/spec/lib/postal/signer_spec.rb b/spec/lib/postal/signer_spec.rb new file mode 100644 index 00000000..34e2ca67 --- /dev/null +++ b/spec/lib/postal/signer_spec.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +require "rails_helper" +module Postal + + RSpec.describe Signer do + STATIC_PRIVATE_KEY = OpenSSL::PKey::RSA.new(2048) # rubocop:disable Lint/ConstantDefinitionInBlock + + subject(:signer) { described_class.new(STATIC_PRIVATE_KEY) } + + describe "#private_key" do + it "returns the private key" do + expect(signer.private_key).to eq(STATIC_PRIVATE_KEY) + end + end + + describe "#public_key" do + it "returns the public key" do + expect(signer.public_key.to_s).to eq(STATIC_PRIVATE_KEY.public_key.to_s) + end + end + + describe "#sign" do + it "returns a valid signature" do + data = "hello world!" + signature = signer.sign(data) + expect(signature).to be_a(String) + verification = STATIC_PRIVATE_KEY.public_key.verify(OpenSSL::Digest.new("SHA256"), + signature, + data) + expect(verification).to be true + end + end + + describe "#sign64" do + it "returns a valid Base64-encoded signature" do + data = "hello world!" + signature = signer.sign64(data) + expect(signature).to be_a(String) + verification = STATIC_PRIVATE_KEY.public_key.verify(OpenSSL::Digest.new("SHA256"), + Base64.strict_decode64(signature), + data) + expect(verification).to be true + end + end + + describe "#jwk" do + it "returns a valid JWK" do + jwk = signer.jwk + expect(jwk).to be_a(JWT::JWK::RSA) + end + end + + describe "#sha1_sign" do + it "returns a valid signature" do + data = "hello world!" + signature = signer.sha1_sign(data) + expect(signature).to be_a(String) + verification = STATIC_PRIVATE_KEY.public_key.verify(OpenSSL::Digest.new("SHA1"), + signature, + data) + expect(verification).to be true + end + end + + describe "#sha1_sign64" do + it "returns a valid Base64-encoded signature" do + data = "hello world!" + signature = signer.sha1_sign64(data) + expect(signature).to be_a(String) + verification = STATIC_PRIVATE_KEY.public_key.verify(OpenSSL::Digest.new("SHA1"), + Base64.strict_decode64(signature), + data) + expect(verification).to be true + end + end + end + +end diff --git a/spec/lib/postal_spec.rb b/spec/lib/postal_spec.rb index 692fde06..abdeefd6 100644 --- a/spec/lib/postal_spec.rb +++ b/spec/lib/postal_spec.rb @@ -5,7 +5,7 @@ RSpec.describe Postal do describe "#signer" do it "returns a signer with the installation's signing key" do - expect(Postal.signer).to be_a(Signer) + expect(Postal.signer).to be_a(Postal::Signer) expect(Postal.signer.private_key.to_pem).to eq OpenSSL::PKey::RSA.new(File.read(Postal::Config.postal.signing_key_path)).to_pem end end diff --git a/spec/lib/signer_spec.rb b/spec/lib/signer_spec.rb deleted file mode 100644 index 948b3c35..00000000 --- a/spec/lib/signer_spec.rb +++ /dev/null @@ -1,76 +0,0 @@ -# frozen_string_literal: true - -require "rails_helper" - -RSpec.describe Signer do - STATIC_PRIVATE_KEY = OpenSSL::PKey::RSA.new(2048) # rubocop:disable Lint/ConstantDefinitionInBlock - - subject(:signer) { described_class.new(STATIC_PRIVATE_KEY) } - - describe "#private_key" do - it "returns the private key" do - expect(signer.private_key).to eq(STATIC_PRIVATE_KEY) - end - end - - describe "#public_key" do - it "returns the public key" do - expect(signer.public_key.to_s).to eq(STATIC_PRIVATE_KEY.public_key.to_s) - end - end - - describe "#sign" do - it "returns a valid signature" do - data = "hello world!" - signature = signer.sign(data) - expect(signature).to be_a(String) - verification = STATIC_PRIVATE_KEY.public_key.verify(OpenSSL::Digest.new("SHA256"), - signature, - data) - expect(verification).to be true - end - end - - describe "#sign64" do - it "returns a valid Base64-encoded signature" do - data = "hello world!" - signature = signer.sign64(data) - expect(signature).to be_a(String) - verification = STATIC_PRIVATE_KEY.public_key.verify(OpenSSL::Digest.new("SHA256"), - Base64.strict_decode64(signature), - data) - expect(verification).to be true - end - end - - describe "#jwk" do - it "returns a valid JWK" do - jwk = signer.jwk - expect(jwk).to be_a(JWT::JWK::RSA) - end - end - - describe "#sha1_sign" do - it "returns a valid signature" do - data = "hello world!" - signature = signer.sha1_sign(data) - expect(signature).to be_a(String) - verification = STATIC_PRIVATE_KEY.public_key.verify(OpenSSL::Digest.new("SHA1"), - signature, - data) - expect(verification).to be true - end - end - - describe "#sha1_sign64" do - it "returns a valid Base64-encoded signature" do - data = "hello world!" - signature = signer.sha1_sign64(data) - expect(signature).to be_a(String) - verification = STATIC_PRIVATE_KEY.public_key.verify(OpenSSL::Digest.new("SHA1"), - Base64.strict_decode64(signature), - data) - expect(verification).to be true - end - end -end