[DRE-commits] r3531 - in packages-wip: . libpassword-ruby libpassword-ruby/branches libpassword-ruby/branches/upstream libpassword-ruby/branches/upstream/current libpassword-ruby/branches/upstream/current/lib

Micah Anderson micah at alioth.debian.org
Thu May 7 13:20:58 UTC 2009


Author: micah
Date: 2009-05-07 13:20:57 +0000 (Thu, 07 May 2009)
New Revision: 3531

Added:
   packages-wip/libpassword-ruby/
   packages-wip/libpassword-ruby/branches/
   packages-wip/libpassword-ruby/branches/upstream/
   packages-wip/libpassword-ruby/branches/upstream/current/
   packages-wip/libpassword-ruby/branches/upstream/current/lib/
   packages-wip/libpassword-ruby/branches/upstream/current/lib/password.rb
Log:
[svn-inject] Installing original source of libpassword-ruby

Added: packages-wip/libpassword-ruby/branches/upstream/current/lib/password.rb
===================================================================
--- packages-wip/libpassword-ruby/branches/upstream/current/lib/password.rb	                        (rev 0)
+++ packages-wip/libpassword-ruby/branches/upstream/current/lib/password.rb	2009-05-07 13:20:57 UTC (rev 3531)
@@ -0,0 +1,432 @@
+# $Id: password.rb,v 1.24 2006/03/02 19:42:33 ianmacd Exp $
+# 
+# Version : 0.5.3
+# Author  : Ian Macdonald <ian at caliban.org>
+#
+# Copyright (C) 2002-2006 Ian Macdonald
+#
+#   This program is free software; you can redistribute it and/or modify
+#   it under the terms of the GNU General Public License as published by
+#   the Free Software Foundation; either version 2, or (at your option)
+#   any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with this program; if not, write to the Free Software Foundation,
+#   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+require 'crack'
+require 'termios'
+
+
+# Ruby/Password is a collection of password handling routines for Ruby,
+# including an interface to CrackLib for the purposes of testing password
+# strength.
+# 
+#  require 'password'
+#
+#  # Define and check a password in code
+#  pw = Password.new( "bigblackcat" )
+#  pw.check
+#
+#  # Get and check a password from the keyboard
+#  begin
+#    password = Password.get( "New password: " )
+#    password.check
+#  rescue Password::WeakPassword => reason
+#    puts reason
+#    retry
+#  end
+#
+#  # Automatically generate and encrypt a password
+#  password = Password.phonemic( 12, Password:ONE_CASE | Password::ONE_DIGIT )
+#  crypted = password.crypt
+#
+#
+class Password < String
+
+  # This exception class is raised if an error occurs during password
+  # encryption when calling Password#crypt.
+  #
+  class CryptError < StandardError; end
+
+  # This exception class is raised if a bad dictionary path is detected by
+  # Password#check.
+  #
+  class DictionaryError < StandardError; end
+
+  # This exception class is raised if a weak password is detected by
+  # Password#check.
+  #
+  class WeakPassword < StandardError; end
+
+  VERSION = '0.5.3'
+
+  # DES algorithm
+  # 
+  DES = true
+
+  # MD5 algorithm (see <em>crypt(3)</em> for more information)
+  #
+  MD5 = false
+ 
+  # This flag is used in conjunction with Password.phonemic and states that a
+  # password must include a digit.
+  #
+  ONE_DIGIT  =	1
+
+  # This flag is used in conjunction with Password.phonemic and states that a
+  # password must include a capital letter.
+  #
+  ONE_CASE    = 1 << 1
+
+  # Characters that may appear in generated passwords. Password.urandom may
+  # also use the characters + and /.
+  #
+  PASSWD_CHARS = '0123456789' +
+		 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
+		 'abcdefghijklmnopqrstuvwxyz'
+
+  # Valid salt characters for use by Password#crypt.
+  #
+  SALT_CHARS   = '0123456789' +
+		 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
+		 'abcdefghijklmnopqrstuvwxyz' +
+		 './'
+
+  # :stopdoc:
+
+  # phoneme flags
+  #
+  CONSONANT = 1
+  VOWEL	    = 1 << 1
+  DIPHTHONG = 1 << 2
+  NOT_FIRST = 1 << 3  # indicates that a given phoneme may not occur first
+
+  PHONEMES = {
+    :a	=> VOWEL,
+    :ae	=> VOWEL      | DIPHTHONG,
+    :ah => VOWEL      | DIPHTHONG,
+    :ai => VOWEL      | DIPHTHONG,
+    :b	=> CONSONANT,
+    :c	=> CONSONANT,
+    :ch	=> CONSONANT  | DIPHTHONG,
+    :d	=> CONSONANT,
+    :e	=> VOWEL,
+    :ee	=> VOWEL      | DIPHTHONG,
+    :ei	=> VOWEL      | DIPHTHONG,
+    :f	=> CONSONANT,
+    :g	=> CONSONANT,
+    :gh	=> CONSONANT  | DIPHTHONG | NOT_FIRST,
+    :h	=> CONSONANT,
+    :i	=> VOWEL,
+    :ie	=> VOWEL      | DIPHTHONG,
+    :j	=> CONSONANT,
+    :k	=> CONSONANT,
+    :l	=> CONSONANT,
+    :m	=> CONSONANT,
+    :n	=> CONSONANT,
+    :ng	=> CONSONANT  | DIPHTHONG | NOT_FIRST,
+    :o	=> VOWEL,
+    :oh	=> VOWEL      | DIPHTHONG,
+    :oo	=> VOWEL      | DIPHTHONG,
+    :p	=> CONSONANT,
+    :ph	=> CONSONANT  | DIPHTHONG,
+    :qu	=> CONSONANT  | DIPHTHONG,
+    :r	=> CONSONANT,
+    :s	=> CONSONANT,
+    :sh	=> CONSONANT  | DIPHTHONG,
+    :t	=> CONSONANT,
+    :th	=> CONSONANT  | DIPHTHONG,
+    :u	=> VOWEL,
+    :v	=> CONSONANT,
+    :w	=> CONSONANT,
+    :x	=> CONSONANT,
+    :y	=> CONSONANT,
+    :z	=> CONSONANT
+  }
+
+  # :startdoc:
+
+
+  # Turn local terminal echo on or off. This method is used for securing the
+  # display, so that a soon to be entered password will not be echoed to the
+  # screen. It is also used for restoring the display afterwards.
+  #
+  # If _masked_ is +true+, the keyboard is put into unbuffered mode, allowing
+  # the retrieval of characters one at a time. _masked_ has no effect when
+  # _on_ is +false+. You are unlikely to need this method in the course of
+  # normal operations.
+  #
+  def Password.echo(on=true, masked=false)
+    term = Termios::getattr( $stdin )
+
+    if on
+      term.c_lflag |= ( Termios::ECHO | Termios::ICANON )
+    else # off
+      term.c_lflag &= ~Termios::ECHO
+      term.c_lflag &= ~Termios::ICANON if masked
+    end
+
+    Termios::setattr( $stdin, Termios::TCSANOW, term )
+  end
+
+
+  # Get a password from _STDIN_, using buffered line input and displaying
+  # _message_ as the prompt. No output will appear while the password is being
+  # typed. Hitting <b>[Enter]</b> completes password entry. If _STDIN_ is not
+  # connected to a tty, no prompt will be displayed.
+  #
+  def Password.get(message="Password: ")
+    begin
+      if $stdin.tty?
+	Password.echo false
+	print message if message
+      end
+
+      pw = Password.new( $stdin.gets || "" )
+      pw.chomp!
+
+    ensure
+      if $stdin.tty?
+	Password.echo true
+	print "\n"
+      end
+    end
+  end
+
+
+  # Get a password from _STDIN_ in unbuffered mode, i.e. one key at a time.
+  # _message_ will be displayed as the prompt and each key press with echo
+  # _mask_ to the terminal. There is no need to hit <b>[Enter]</b> at the end.
+  #
+  def Password.getc(message="Password: ", mask='*')
+    # Save current buffering mode
+    buffering = $stdout.sync
+
+    # Turn off buffering
+    $stdout.sync = true
+
+    begin
+      Password.echo(false, true)
+      print message if message
+      pw = ""
+
+      while ( char = $stdin.getc ) != 10 # break after [Enter]
+	putc mask
+	pw << char
+      end
+
+    ensure
+      Password.echo true
+      print "\n"
+    end
+
+    # Restore original buffering mode
+    $stdout.sync = buffering
+
+    Password.new( pw )
+  end
+
+
+  # :stopdoc:
+  
+  # Determine whether next character should be a vowel or consonant.
+  #
+  def Password.get_vowel_or_consonant
+    rand( 2 ) == 1 ? VOWEL : CONSONANT
+  end
+
+  # :startdoc:
+
+
+  # Generate a memorable password of _length_ characters, using phonemes that
+  # a human-being can easily remember. _flags_ is one or more of
+  # <em>Password::ONE_DIGIT</em> and <em>Password::ONE_CASE</em>, logically
+  # OR'ed together. For example:
+  #
+  #  pw = Password.phonemic( 8, Password::ONE_DIGIT | Password::ONE_CASE )
+  #
+  # This would generate an eight character password, containing a digit and an
+  # upper-case letter, such as <b>Ug2shoth</b>.
+  #
+  # This method was inspired by the
+  # pwgen[http://sourceforge.net/projects/pwgen/] tool, written by Theodore
+  # Ts'o.
+  #
+  # Generated passwords may contain any of the characters in
+  # <em>Password::PASSWD_CHARS</em>.
+  #
+  def Password.phonemic(length=8, flags=nil)
+
+    pw = nil
+    ph_flags = flags
+
+    loop do
+
+      pw = ""
+
+      # Separate the flags integer into an array of individual flags
+      feature_flags = [ flags & ONE_DIGIT, flags & ONE_CASE ]
+
+      prev = []
+      first = true
+      desired = Password.get_vowel_or_consonant
+
+      # Get an Array of all of the phonemes
+      phonemes = PHONEMES.keys.map { |ph| ph.to_s }
+      nr_phonemes = phonemes.size
+
+      while pw.length < length do
+
+	# Get a random phoneme and its length
+	phoneme = phonemes[ rand( nr_phonemes ) ]
+	ph_len = phoneme.length
+
+	# Get its flags as an Array
+	ph_flags = PHONEMES[ phoneme.to_sym ]
+	ph_flags = [ ph_flags & CONSONANT, ph_flags & VOWEL,
+		     ph_flags & DIPHTHONG, ph_flags & NOT_FIRST ]
+
+	# Filter on the basic type of the next phoneme
+	next if ph_flags.include? desired
+
+	# Handle the NOT_FIRST flag
+	next if first and ph_flags.include? NOT_FIRST
+
+	# Don't allow a VOWEL followed a vowel/diphthong pair
+	next if prev.include? VOWEL and ph_flags.include? VOWEL and
+		ph_flags.include? DIPHTHONG
+
+	# Don't allow us to go longer than the desired length
+	next if ph_len > length - pw.length
+
+	# We've found a phoneme that meets our criteria
+	pw << phoneme
+
+	# Handle ONE_CASE
+	if feature_flags.include? ONE_CASE
+
+	  if (first or ph_flags.include? CONSONANT) and rand( 10 ) < 3
+	    pw[-ph_len, 1] = pw[-ph_len, 1].upcase
+	    feature_flags.delete ONE_CASE
+	  end
+
+	end
+
+	# Is password already long enough?
+	break if pw.length >= length
+
+	# Handle ONE_DIGIT
+	if feature_flags.include? ONE_DIGIT
+
+	  if ! first and rand( 10 ) < 3
+	    pw << ( rand( 10 ) + ?0 ).chr
+	    feature_flags.delete ONE_DIGIT
+
+	    first = true
+	    prev = []
+	    desired = Password.get_vowel_or_consonant
+	    next
+	  end
+
+	end
+
+	if desired == CONSONANT
+	  desired = VOWEL
+	elsif prev.include? VOWEL or ph_flags.include? DIPHTHONG or
+	      rand(10) > 3
+	  desired = CONSONANT
+	else
+	  desired = VOWEL
+	end
+
+	prev = ph_flags
+	first = false
+      end
+
+      # Try again
+      break unless feature_flags.include? ONE_CASE or
+		   feature_flags.include? ONE_DIGIT
+
+    end
+
+    Password.new( pw )
+
+  end
+
+
+  # Generate a random password of _length_ characters. Unlike the
+  # Password.phonemic method, no attempt will be made to generate a memorable
+  # password. Generated passwords may contain any of the characters in
+  # <em>Password::PASSWD_CHARS</em>.
+  #
+  #
+  def Password.random(length=8)
+    pw = ""
+    nr_chars = PASSWD_CHARS.size
+
+    length.times { pw << PASSWD_CHARS[ rand( nr_chars ) ] }
+
+    Password.new( pw )
+  end
+
+
+  # An alternative to Password.random. It uses the <tt>/dev/urandom</tt>
+  # device to generate passwords, returning +nil+ on systems that do not
+  # implement the device. The passwords it generates may contain any of the
+  # characters in <em>Password::PASSWD_CHARS</em>, plus the additional
+  # characters + and /.
+  #
+  def Password.urandom(length=8)
+    return nil unless File.chardev? '/dev/urandom'
+
+    rand_data = nil
+    File.open( "/dev/urandom" ) { |f| rand_data = f.read( length ) }
+
+    # Base64 encode it
+    Password.new( [ rand_data ].pack( 'm' )[ 0 .. length - 1 ] )
+  end
+
+
+  # Encrypt a password using _type_ encryption. _salt_, if supplied, will be
+  # used to perturb the encryption algorithm and should be chosen from the
+  # <em>Password::SALT_CHARS</em>. If no salt is given, a randomly generated
+  # salt will be used.
+  #
+  def crypt(type=DES, salt='')
+
+    unless ( salt.split( // ) - SALT_CHARS.split( // ) ).empty?
+      raise CryptError, 'bad salt'
+    end
+
+    salt = Password.random( type ? 2 : 8 ) if salt.empty?
+
+    # (Linux glibc2 interprets a salt prefix of '$1$' as a call to use MD5
+    # instead of DES when calling crypt(3))
+    salt = '$1$' + salt if type == MD5
+
+    # Pass to crypt in class String (our parent class)
+    crypt = super( salt )
+
+    # Raise an exception if MD5 was wanted, but result is not recognisable
+    if type == MD5 && crypt !~ /^\$1\$/
+      raise CryptError, 'MD5 not implemented'
+    end
+
+    crypt
+  end
+
+end
+
+
+# Display a phonemic password, if run directly.
+#
+if $0 == __FILE__
+  puts Password.phonemic
+end


Property changes on: packages-wip/libpassword-ruby/branches/upstream/current/lib/password.rb
___________________________________________________________________
Added: svn:executable
   + 




More information about the Pkg-ruby-extras-commits mailing list