grempe/secretsharing

View on GitHub
lib/secretsharing/shamir/container.rb

Summary

Maintainability
A
1 hr
Test Coverage
# -*- encoding: utf-8 -*-

# Copyright 2011-2015 Alexander Klink and Glenn Rempe

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

#     http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

module SecretSharing
  module Shamir
    # The SecretSharing::Shamir::Container class can be used to share random
    # secrets between n people, so that k < n people can recover the
    # secret, but k-1 people learn nothing (in an information-theoretical
    # sense) about the secret.
    #
    # For a theoretical background, see:
    #   http://www.cs.tau.ac.il/~bchor/Shamir.html
    #   http://en.wikipedia.org/wiki/Secret_sharing#Shamir.27s_scheme
    #
    class Container
      include SecretSharing::Shamir
      attr_reader :n, :k, :secret, :shares

      MIN_SHARES               = 2
      MAX_SHARES               = 512

      # To create a new SecretSharing::Shamir::Container object, you can
      # pass either just n, or n and k where:
      #
      #   n = The total number of shares that will be created.
      #   k = The threshold number of the total shares needed to
      #       recreate the original secret. (Default = n)
      #
      # For example:
      #
      #   # 3(k) out of 5(n) shares needed to recover secret
      #   s = SecretSharing::Shamir::Container.new(5, 3)
      #
      #   # 3(k) out of 3(n) shares needed to recover secret
      #   s = SecretSharing::Shamir::Container.new(3)
      #
      def initialize(n, k = n)
        @n               = n.to_i
        @k               = k.to_i

        fail ArgumentError, 'n must be an Integer' unless @n.is_a?(Integer)
        fail ArgumentError, 'k must be an Integer' unless @k.is_a?(Integer)

        fail ArgumentError, 'k must be <= n'              unless @k <= @n
        fail ArgumentError, 'k must be >= #{MIN_SHARES}'  unless @k >= MIN_SHARES
        fail ArgumentError, 'n must be <= #{MAX_SHARES}'  unless @n <= MAX_SHARES

        @secret          = nil
        @shares          = []
      end

      def secret?
        @secret.is_a?(SecretSharing::Shamir::Secret)
      end

      def secret=(sec)
        fail ArgumentError, 'secret has already been set' if secret?
        fail ArgumentError, 'secret must be a SecretSharing::Shamir::Secret instance' unless sec.is_a?(SecretSharing::Shamir::Secret)
        @secret = sec
        @shares = Share.create_shares(@k, @n, @secret)
        true
      end

      # Add a secret share to the object. Accepts a
      # SecretSharing::Shamir::Share instance.
      # Returns secret as a SecretSharing::Shamir::Secret if enough valid shares have been added
      # to recover the secret, and false otherwise. The secret can also be recovered
      # later with SecretSharing::Shamir::Container#secret if enough valid shares were previously
      # provided.
      def <<(share)
        # You can't add more shares than were originally generated with value of @n
        fail ArgumentError, 'You have added more shares than allowed by the value of @n' if @shares.size >= @n

        share = SecretSharing::Shamir::Share.new(:share => share) unless share.is_a?(SecretSharing::Shamir::Share)
        @shares << share unless @shares.include?(share)
        @secret = Share.recover_secret(@shares)
      end
    end # class Container
  end # module Shamir
end # module SecretSharing