<commit_before>#
# Copyright (c) 2009-2011 RightScale Inc
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

module RightScale
  
  # Serializer implementation which secures messages by using
  # X.509 certificate signing
  class SecureSerializer

    class MissingCertificate < Exception; end
    class InvalidSignature < Exception; end

    # Initialize serializer, must be called prior to using it
    #
    # === Parameters
    # serializer(Serializer):: Object serializer
    # identity(String):: Serialized identity associated with serialized messages
    # cert(String):: Certificate used to sign and decrypt serialized messages
    # key(RsaKeyPair):: Private key corresponding to specified cert
    # store(Object):: Certificate store exposing certificates used for
    #   encryption (get_recipients) and signature validation (get_signer)
    # encrypt(Boolean):: true if data should be signed and encrypted, otherwise
    #   just signed, true by default
    def self.init(serializer, identity, cert, key, store, encrypt = true)
      @identity = identity
      @cert = cert
      @key = key
      @store = store
      @encrypt = encrypt
      @serializer = serializer
    end
    
    # Was serializer initialized?
    def self.initialized?
      @identity && @cert && @key && @store
    end

    # Serialize, sign, and encrypt message
    # Sign and encrypt using X.509 certificate
    #
    # === Parameters
    # obj(Object):: Object to be serialized and encrypted
    # encrypt(Boolean|nil):: true if object should be signed and encrypted,
    #   false if just signed, nil means use class setting
    #
    # === Return
    # (String):: MessagePack serialized and optionally encrypted object
    #
    # === Raise
    # Exception:: If certificate identity, certificate store, certificate, or private key missing
    def self.dump(obj, encrypt = nil)
      raise "Missing certificate identity" unless @identity
      raise "Missing certificate" unless @cert
      raise "Missing certificate key" unless @key
      raise "Missing certificate store" unless @store || !@encrypt
      must_encrypt = encrypt.nil? ? @encrypt : encrypt
      serialize_format = if obj.respond_to?(:send_version) && obj.send_version >= 12
        @serializer.format
      else
        :json
      end
      encode_format = serialize_format == :json ? :pem : :der
      msg = @serializer.dump(obj, serialize_format)
      if must_encrypt
        certs = @store.get_recipients(obj)
        if certs
          msg = EncryptedDocument.new(msg, certs).encrypted_data(encode_format)
        else
          target = obj.target_for_encryption if obj.respond_to?(:target_for_encryption)
          Log.warning("No certs available for object #{obj.class} being sent to #{target.inspect}\n") if target
        end
      end
      sig = Signature.new(msg, @cert, @key).data(encode_format)
      @serializer.dump({'id' => @identity, 'data' => msg, 'signature' => sig, 'encrypted' => !certs.nil?}, serialize_format)
    end
    
    # Decrypt, authorize signature, and unserialize message
    # Use x.509 certificate store for decrypting and validating signature
    #
    # === Parameters
    # msg(String):: Serialized and optionally encrypted object using MessagePack or JSON
    #
    # === Return
    # (Object):: Unserialized object
    #
    # === Raise
    # Exception:: If certificate store, certificate, or private key missing
    # MissingCertificate:: If could not find certificate for message signer
    # InvalidSignature:: If message signature check failed for message
    def self.load(msg)
      raise "Missing certificate store" unless @store
      raise "Missing certificate" unless @cert || !@encrypt
      raise "Missing certificate key" unless @key || !@encrypt

      msg = @serializer.load(msg)
      sig = Signature.from_data(msg['signature'])
      certs = @store.get_signer(msg['id'])
      raise MissingCertificate.new("Could not find a certificate for signer #{msg['id']}") unless certs

      certs = [ certs ] unless certs.respond_to?(:any?)
      raise InvalidSignature.new("Failed signature check for signer #{msg['id']}") unless certs.any? { |c| sig.match?(c) }

      data = msg['data']
      if data && @encrypt && msg['encrypted']
        data = EncryptedDocument.from_data(data).decrypted_data(@key, @cert)
      end
      @serializer.load(data) if data
    end

  end # SecureSerializer

end # RightScale<commit_msg>make secure serializer an object, keep class methods to support current functionality, just delegate the calls to the internal secure serializer<commit_after>#
# Copyright (c) 2009-2011 RightScale Inc
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

module RightScale
  
  # Serializer implementation which secures messages by using
  # X.509 certificate signing
  class SecureSerializer

    class MissingCertificate < Exception; end
    class InvalidSignature < Exception; end

    # create the one and only SecureSerializer
    def self.init(serializer, identity, cert, key, store, encrypt = true)
      @serializer = SecureSerializer.new(serializer, identity, cert, key, store, encrypt)
      true
    end

    # Was serializer initialized?
    def self.initialized?
      !@serializer.nil?
    end

    # see SecureSerializer::dump
    def self.dump(obj, encrypt = nil)
      @serializer.dump(obj, encrypt)
    end

    # see SecureSerializer::load
    def self.load(msg)
      @serializer.load(msg)
    end

    # Initialize serializer, must be called prior to using it
    #
    # === Parameters
    # serializer(Serializer):: Object serializer
    # identity(String):: Serialized identity associated with serialized messages
    # cert(String):: Certificate used to sign and decrypt serialized messages
    # key(RsaKeyPair):: Private key corresponding to specified cert
    # store(Object):: Certificate store exposing certificates used for
    #   encryption (get_recipients) and signature validation (get_signer)
    # encrypt(Boolean):: true if data should be signed and encrypted, otherwise
    #   just signed, true by default
    def initialize(serializer, identity, cert, key, store, encrypt = true)
      @identity = identity
      @cert = cert
      @key = key
      @store = store
      @encrypt = encrypt
      @serializer = serializer
    end

    # Serialize, sign, and encrypt message
    # Sign and encrypt using X.509 certificate
    #
    # === Parameters
    # obj(Object):: Object to be serialized and encrypted
    # encrypt(Boolean|nil):: true if object should be signed and encrypted,
    #   false if just signed, nil means use class setting
    #
    # === Return
    # (String):: MessagePack serialized and optionally encrypted object
    #
    # === Raise
    # Exception:: If certificate identity, certificate store, certificate, or private key missing
    def dump(obj, encrypt = nil)
      raise "Missing certificate identity" unless @identity
      raise "Missing certificate" unless @cert
      raise "Missing certificate key" unless @key
      raise "Missing certificate store" unless @store || !@encrypt
      must_encrypt = encrypt.nil? ? @encrypt : encrypt
      serialize_format = if obj.respond_to?(:send_version) && obj.send_version >= 12
        @serializer.format
      else
        :json
      end
      encode_format = serialize_format == :json ? :pem : :der
      msg = @serializer.dump(obj, serialize_format)
      if must_encrypt
        certs = @store.get_recipients(obj)
        if certs
          msg = EncryptedDocument.new(msg, certs).encrypted_data(encode_format)
        else
          target = obj.target_for_encryption if obj.respond_to?(:target_for_encryption)
          Log.warning("No certs available for object #{obj.class} being sent to #{target.inspect}\n") if target
        end
      end
      sig = Signature.new(msg, @cert, @key).data(encode_format)
      @serializer.dump({'id' => @identity, 'data' => msg, 'signature' => sig, 'encrypted' => !certs.nil?}, serialize_format)
    end
    
    # Decrypt, authorize signature, and unserialize message
    # Use x.509 certificate store for decrypting and validating signature
    #
    # === Parameters
    # msg(String):: Serialized and optionally encrypted object using MessagePack or JSON
    #
    # === Return
    # (Object):: Unserialized object
    #
    # === Raise
    # Exception:: If certificate store, certificate, or private key missing
    # MissingCertificate:: If could not find certificate for message signer
    # InvalidSignature:: If message signature check failed for message
    def load(msg)
      raise "Missing certificate store" unless @store
      raise "Missing certificate" unless @cert || !@encrypt
      raise "Missing certificate key" unless @key || !@encrypt

      msg = @serializer.load(msg)
      sig = Signature.from_data(msg['signature'])
      certs = @store.get_signer(msg['id'])
      raise MissingCertificate.new("Could not find a certificate for signer #{msg['id']}") unless certs

      certs = [ certs ] unless certs.respond_to?(:any?)
      raise InvalidSignature.new("Failed signature check for signer #{msg['id']}") unless certs.any? { |c| sig.match?(c) }

      data = msg['data']
      if data && @encrypt && msg['encrypted']
        data = EncryptedDocument.from_data(data).decrypted_data(@key, @cert)
      end
      @serializer.load(data) if data
    end

  end # SecureSerializer

end # RightScale
{'fraction_non_alphanumeric': 0.08684993871476099, 'fraction_numerical': 0.0033269129749606023, 'mean_word_length': 3.4070216049382718, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '666659', 'n_tokens_mistral': 3099, 'n_tokens_neox': 2958, 'n_words': 1176}