ppp 0.1.2.alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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