ppp 0.1.2.alpha

Sign up to get free protection for your applications and to get access to all the features.
data/ext/ppp/sha2.h ADDED
@@ -0,0 +1,108 @@
1
+ /*
2
+ * FIPS 180-2 SHA-224/256/384/512 implementation
3
+ * Last update: 02/02/2007
4
+ * Issue date: 04/30/2005
5
+ *
6
+ * Copyright (C) 2005, 2007 Olivier Gay <olivier.gay@a3.epfl.ch>
7
+ * All rights reserved.
8
+ *
9
+ * Redistribution and use in source and binary forms, with or without
10
+ * modification, are permitted provided that the following conditions
11
+ * are met:
12
+ * 1. Redistributions of source code must retain the above copyright
13
+ * notice, this list of conditions and the following disclaimer.
14
+ * 2. Redistributions in binary form must reproduce the above copyright
15
+ * notice, this list of conditions and the following disclaimer in the
16
+ * documentation and/or other materials provided with the distribution.
17
+ * 3. Neither the name of the project nor the names of its contributors
18
+ * may be used to endorse or promote products derived from this software
19
+ * without specific prior written permission.
20
+ *
21
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
22
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
25
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31
+ * SUCH DAMAGE.
32
+ */
33
+
34
+ #ifndef SHA2_H
35
+ #define SHA2_H
36
+
37
+ #define SHA224_DIGEST_SIZE ( 224 / 8)
38
+ #define SHA256_DIGEST_SIZE ( 256 / 8)
39
+ #define SHA384_DIGEST_SIZE ( 384 / 8)
40
+ #define SHA512_DIGEST_SIZE ( 512 / 8)
41
+
42
+ #define SHA256_BLOCK_SIZE ( 512 / 8)
43
+ #define SHA512_BLOCK_SIZE (1024 / 8)
44
+ #define SHA384_BLOCK_SIZE SHA512_BLOCK_SIZE
45
+ #define SHA224_BLOCK_SIZE SHA256_BLOCK_SIZE
46
+
47
+ #ifndef SHA2_TYPES
48
+ #define SHA2_TYPES
49
+ typedef unsigned char uint8;
50
+ typedef unsigned int uint32;
51
+ typedef unsigned long long uint64;
52
+ #endif
53
+
54
+ #ifdef __cplusplus
55
+ extern "C" {
56
+ #endif
57
+
58
+ typedef struct {
59
+ unsigned int tot_len;
60
+ unsigned int len;
61
+ unsigned char block[2 * SHA256_BLOCK_SIZE];
62
+ uint32 h[8];
63
+ } sha256_ctx;
64
+
65
+ typedef struct {
66
+ unsigned int tot_len;
67
+ unsigned int len;
68
+ unsigned char block[2 * SHA512_BLOCK_SIZE];
69
+ uint64 h[8];
70
+ } sha512_ctx;
71
+
72
+ typedef sha512_ctx sha384_ctx;
73
+ typedef sha256_ctx sha224_ctx;
74
+
75
+ void sha224_init(sha224_ctx *ctx);
76
+ void sha224_update(sha224_ctx *ctx, const unsigned char *message,
77
+ unsigned int len);
78
+ void sha224_final(sha224_ctx *ctx, unsigned char *digest);
79
+ void sha224(const unsigned char *message, unsigned int len,
80
+ unsigned char *digest);
81
+
82
+ void sha256_init(sha256_ctx * ctx);
83
+ void sha256_update(sha256_ctx *ctx, const unsigned char *message,
84
+ unsigned int len);
85
+ void sha256_final(sha256_ctx *ctx, unsigned char *digest);
86
+ void sha256(const unsigned char *message, unsigned int len,
87
+ unsigned char *digest);
88
+
89
+ void sha384_init(sha384_ctx *ctx);
90
+ void sha384_update(sha384_ctx *ctx, const unsigned char *message,
91
+ unsigned int len);
92
+ void sha384_final(sha384_ctx *ctx, unsigned char *digest);
93
+ void sha384(const unsigned char *message, unsigned int len,
94
+ unsigned char *digest);
95
+
96
+ void sha512_init(sha512_ctx *ctx);
97
+ void sha512_update(sha512_ctx *ctx, const unsigned char *message,
98
+ unsigned int len);
99
+ void sha512_final(sha512_ctx *ctx, unsigned char *digest);
100
+ void sha512(const unsigned char *message, unsigned int len,
101
+ unsigned char *digest);
102
+
103
+ #ifdef __cplusplus
104
+ }
105
+ #endif
106
+
107
+ #endif /* !SHA2_H */
108
+
data/lib/ppp.rb ADDED
@@ -0,0 +1,41 @@
1
+ require 'digest'
2
+ require "ppp/version"
3
+ require "ppp/generator"
4
+ require 'ppp/Cppp'
5
+ require 'ppp/card/base'
6
+ require 'ppp/card/html'
7
+ require 'ppp/card/plain'
8
+
9
+ module Ppp
10
+ class << self
11
+ @@ALPHABETS = { :conservative => '!#%+23456789:=?@ABCDEFGHJKLMNPRSTUVWXYZabcdefghijkmnopqrstuvwxyz',
12
+ :aggressive => '!"#$%&\'()*+,-./23456789:;<=>?@ABCDEFGHJKLMNOPRSTUVWXYZ[\]^_abcdefghijkmnopqrstuvwxyz{|}~' }
13
+
14
+ # @return [Ppp::Generator] with the given SHA-256 key
15
+ def code_generator key, opts
16
+ Generator.new key, opts
17
+ end
18
+
19
+ # @return a SHA-256 digest of the given string
20
+ def key_from_string str
21
+ Digest::SHA256.new.update( str ).to_s
22
+ end
23
+
24
+ # @return a random SHA-256 key
25
+ def random_key
26
+ Cppp.random_key
27
+ end
28
+
29
+ def printer style, *args
30
+ case style
31
+ when :html then return Card::Html.new *args
32
+ when :plain then return Card::Plain.new *args
33
+ end
34
+ raise ArgumentError.new( "%s is not a valid printer style." % style )
35
+ end
36
+
37
+ def default_alphabets
38
+ @@ALPHABETS
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,50 @@
1
+ require 'ppp/generator'
2
+
3
+ module Ppp
4
+ module Card
5
+ class Base
6
+ @@CHARS_PER_LINE = 34
7
+ @@FIRST_COLUMN = ?A
8
+ @@ROW_COL_PATTERN = /[[:digit:]][[:alpha:]]/
9
+
10
+ @@ERROR_BAD_ROW_COL = %[Expected a string with exactly one digit and one letter, got "%s".]
11
+ @@ERROR_LONG_CODES = %[Passcodes longer than 16 characters are too long for printing]
12
+
13
+ def initialize generator, opts={}
14
+ @generator = generator
15
+ raise ArgumentError.new( @@ERROR_LONG_CODES ) if code_length > 16
16
+
17
+ options = { :card_title => 'PPP Passcard', :first_card_index => 1 }.merge opts
18
+ @title = options[ :card_title ]
19
+ @card_index = options[ :first_card_index ]
20
+ @offset = 0
21
+ end
22
+
23
+ def code_length
24
+ @generator.length
25
+ end
26
+
27
+ def passcodes_per_line
28
+ @passcodes_per_line ||= ( (@@CHARS_PER_LINE+1) / (code_length + 1) ).to_i
29
+ end
30
+
31
+ def next_code
32
+ code = @generator.passcode( @offset )
33
+ @offset += 1
34
+ code
35
+ end
36
+
37
+ def passcode row_col
38
+ raise ArgumentError.new( @@ERROR_BAD_ROW_COL % row_col ) unless row_col.size == 2 && @@ROW_COL_PATTERN.match( row_col.split('').sort.join )
39
+ passcode *row_col.split('')
40
+ end
41
+
42
+ def passcode row, col
43
+ col_offset = col.ord - @@FIRST_COLUMN.ord
44
+ row_offset = row - 1
45
+
46
+ @generator.passcode row_offset * passcodes_per_line + col_offset
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,126 @@
1
+ require 'ppp/generator'
2
+ require 'ppp/card/base'
3
+
4
+ class Ppp::Card::Html < Ppp::Card::Base
5
+ @@SIZES = {:creditcard => %w[ 85.60mm 53.98mm ]}
6
+
7
+ def initialize generator, opts={}
8
+ super
9
+
10
+ options = { :font_size => 14, :size => :creditcard }.merge opts
11
+ @font_size = options[ :font_size ]
12
+ @size = process_size options[ :size ]
13
+ end
14
+
15
+ def css
16
+ %[
17
+ <style>
18
+ .card, .card .title, .card .card_index, .card .codes, .card .codes td {
19
+ font-family: monospace;
20
+ font-size: 3.5mm;
21
+ }
22
+ .card {
23
+ background-color: \#fff;
24
+ margin: 0 auto 0 auto;
25
+ width: #{width};
26
+ height: #{height};
27
+ border: 1px solid \#000;
28
+ border-top: none;
29
+ border-spacing: 0;
30
+ }
31
+ .title {
32
+ border: 1px solid \#000;
33
+ border-bottom: 1px solid \#000;
34
+ background-color: \#eee;
35
+ text-align: left;
36
+ padding-left: 1ex;
37
+ font-weight: bold;
38
+ }
39
+ .title td:first-child {
40
+ width: 2.5em;
41
+ }
42
+ .card_index {
43
+ float: right;
44
+ margin-right: 1ex;
45
+ }
46
+ .card_index:before { content: '['; }
47
+ .card_index:after { content: ']'; }
48
+
49
+ .codes_heading th {
50
+ text-align: center;
51
+ width: #{code_length}em;
52
+ border-bottom: 1px solid \#aaa;
53
+ background-color: \#eee;
54
+ }
55
+ .codes_heading th:first-child {
56
+ background-color: \#fff;
57
+ border-right: 1px solid \#aaa;
58
+ }
59
+ .codes_heading th:last-child { border-right: none; }
60
+ .codes td {
61
+ padding: 0;
62
+ margin: 0;
63
+ text-align: center;
64
+ border-right: 1px solid \#aaa;
65
+ border-bottom: 1px solid \#aaa;
66
+ }
67
+ .codes tr td:first-child {
68
+ text-align: right;
69
+ padding-right: 0.5em;
70
+ font-weight: bold;
71
+ background-color: \#eee;
72
+ border-right: 1px solid \#aaa;
73
+ }
74
+ .codes tr td:first-child { border-bottom: none; }
75
+ .codes tr td:last-child { border-right: none; }
76
+ .codes tr:last-child td { border-bottom: none; }
77
+ </style>
78
+ ]
79
+ end
80
+
81
+ def html
82
+ %[
83
+ <table class="card">
84
+ <caption class="title">
85
+ #{@title}
86
+ <span class="card_index">#{@card_index}</span>
87
+ </caption>
88
+ <colgroup span="1">
89
+ <colgroup span="#{passcodes_per_line}">
90
+ <thead class="codes_heading">
91
+ <th>&nbsp;</th>
92
+ #{ (0..passcodes_per_line-1).collect { |i| %[<th>#{(@@FIRST_COLUMN.ord + i).chr}</th>] }.join ?\n }
93
+ </thead>
94
+ <tbody class="codes">
95
+ #{ (1..9).collect { |i| "<tr><td>#{i}</td>#{(1..passcodes_per_line).collect {%[<td>#{next_code}</td>]}.join(?\n) }</tr>" }.join(?\n) }
96
+ </tbody>
97
+ </table>
98
+ ]
99
+ end
100
+
101
+ def to_s
102
+ "#{css}\n\n#{html}"
103
+ end
104
+
105
+ protected # ----------------------------------------------
106
+
107
+ def width
108
+ @size[0]
109
+ end
110
+
111
+ def height
112
+ @size[1]
113
+ end
114
+
115
+ def process_size size
116
+ case size
117
+ when Symbol
118
+ return @@SIZES[ size ] if @@SIZES.include? size
119
+ raise ArgumentError.new( %[No card size for symbol "#{size}"] )
120
+ when Array
121
+ return [ size[0].to_s, size[1].to_s ]
122
+ else
123
+ raise ArgumentError.new( "Size must be a Symbol or Array" )
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,91 @@
1
+ require 'ppp/card/base'
2
+
3
+ class Ppp::Card::Plain < Ppp::Card::Base
4
+
5
+ def initialize generator, opts={}
6
+ super
7
+
8
+ options = { :row_count => 10 }.merge opts
9
+ @row_count = options[ :row_count ]
10
+ end
11
+
12
+ def to_s
13
+ line( bar ) +
14
+ line( :pad, title_str ) +
15
+ line( split_bar ) +
16
+ line( :pad, header ) +
17
+ row_lines.join( '' ) +
18
+ line( split_bar )
19
+ end
20
+
21
+ private
22
+
23
+ def bar
24
+ @bar ||= '-' * (width + 2)
25
+ end
26
+
27
+ def width
28
+ @width ||= begin
29
+ return rows.first.join( ' ' ).size if rows.size >= 1
30
+ header.size
31
+ end
32
+ end
33
+
34
+ def rows
35
+ @rows ||= begin
36
+ (1..@row_count).collect do |i|
37
+ [ first_column( i ) ] + (1..passcodes_per_line).collect {next_code}
38
+ end
39
+ end
40
+ end
41
+
42
+ def header
43
+ @header ||= header_cells.join ' '
44
+ end
45
+
46
+ def header_cells
47
+ [' ' * first_column_width ] + (1..passcodes_per_line).collect do |i|
48
+ (@@FIRST_COLUMN.ord + i-1).chr.center code_length
49
+ end
50
+ end
51
+
52
+ def title_str
53
+ @title_str ||= begin
54
+ title = @title[0, width - card_label.size] # trim title if it's too long to fit
55
+
56
+ blank_space = width - (title.size + card_label.size)
57
+ title = title + (' ' * blank_space) + card_label
58
+ end
59
+ end
60
+
61
+ def card_label
62
+ @card_label ||= "[#{@card_index}]"
63
+ end
64
+
65
+ def split_bar
66
+ @split_bar ||= begin
67
+ parts = ['-' * first_column_width] + (1..passcodes_per_line).collect { '-' * code_length }
68
+ "-#{parts.join '+'}-"
69
+ end
70
+ end
71
+
72
+ def row_lines
73
+ @row_lines ||= rows.collect{ |r| line :pad, r.join( ' ' ) }
74
+ end
75
+
76
+ def line pad=:nopad, str
77
+ return "| #{str} |\n" if pad == :pad
78
+
79
+ "|#{str}|\n"
80
+ end
81
+
82
+ def first_column row_index
83
+ label = "#{row_index}:"
84
+ padding = ' ' * (first_column_width - label.size)
85
+ padding + label
86
+ end
87
+
88
+ def first_column_width
89
+ @first_column_width ||= @row_count.to_s.size + 1
90
+ end
91
+ end
@@ -0,0 +1,69 @@
1
+ require 'ppp/Cppp'
2
+
3
+ # Generates passcodes.
4
+ class Ppp::Generator
5
+ attr_reader :seed, :length, :alphabet
6
+
7
+ @@HEX_PATTERN = /[a-fA-F0-9]{64}/
8
+
9
+ # @param [String] sha256_key a 64 hex-digit string representation of a
10
+ # SHA-256 hash. This hash will seed all the generated passcodes.
11
+ # @param [Fixnum] length the number of characters in each generated passcode
12
+ # @param [String] alphabet a string containing the characters passcodes will
13
+ # be comprised of. Cannot contain null characters and duplicate characters
14
+ # will be removed.
15
+ def initialize sha256_key, opts={}
16
+ raise NotHexKey.new( sha256_key ) if @@HEX_PATTERN.match( sha256_key ).nil?
17
+
18
+ @seed = sha256_key
19
+
20
+ options = { :length => 4, :alphabet => :conservative }.merge opts
21
+ @length = options[ :length ]
22
+ @alphabet = process_alphabet( options[ :alphabet ] ).split( '' ).uniq.join
23
+
24
+ raise ArgumentError.new( "alphabet cannot contain null character" ) if alphabet.include? ?\0
25
+ end
26
+
27
+ # Creates passcodes seeded off the SHA-256 key this object was created with.
28
+ # Calling this method subsequent times with the same offset will return the
29
+ # same passcodes, so you should increase the offset by count each time.
30
+ # @param [Fixnum] offset the number of passcodes to skip
31
+ # @param [Fixnum] count the number of passcodes to return
32
+ # @return [Array] an array of passcodes
33
+ def passcodes offset, count
34
+ Cppp.passcodes @seed, offset, count, @length, @alphabet
35
+ end
36
+
37
+ # (@see #passcodes)
38
+ def passcode offset
39
+ passcodes( offset, 1 ).first
40
+ end
41
+
42
+ # Check if a given passcode is correct
43
+ # @param [Fixnum] index the index of the passcode to check against
44
+ # @return [Boolean] if the given passcode matches the passcode at the given offset
45
+ def verify index, given_passcode
46
+ passcode( index ) == given_passcode
47
+ end
48
+
49
+ private
50
+
51
+ def process_alphabet alphabet
52
+ case alphabet
53
+ when Symbol
54
+ return Ppp.default_alphabets[ alphabet ] if Ppp.default_alphabets.include? alphabet
55
+ raise ArgumentError.new( %[No alphabet for for symbol "#{alphabet}"] )
56
+ when String
57
+ return alphabet
58
+ else
59
+ raise ArgumentError.new( "Alphabet must be a Symbol or String" )
60
+ end
61
+ end
62
+
63
+ class NotHexKey < ArgumentError
64
+ @@error = 'Expected a 64 digit hex-string, but got "%s". Use Ppp.key_from_string() to generate a useable hex-string from an arbitrary string.'
65
+ def initialize( key ) @key = key end
66
+
67
+ def to_s() @@error % @key end
68
+ end
69
+ end