Class: Cipher::DES

Inherits:
Object
  • Object
show all
Defined in:
lib/cipher/des.rb

Overview

Brief

The Cipher::DES class allows for encryption and decryption of plain text using the “Data Encryption Standard”. This version is the modified version which is part of the VNC authentication scheme.

Usage is pretty straight forward:

des = Cipher::DES.new 'mysecretkey', :encrypt
str = des.update 'plain text'
str << des.update 'more plain text'
str << final

Or just use the shortcut class methods:

str = Cipher::DES.encrypt 'mysecretkey', 'plain text'

About

This code was ported from the file “d3des.c”, for portability reasons. It is not expected to be quick, but is only being used currently for the VNC authentication handshake. If you wanted to cipher a lot of text, you should probably compile the original C as an extension.

I’ve included the following copyright info from the C source verbatim:

This is D3DES (V5.09) by Richard Outerbridge with the double and
triple-length support removed for use in VNC.  Also the bytebit[] array
has been reversed so that the most significant bit in each byte of the
key is ignored, not the least significant.

These changes are:
Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.

This software 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.

D3DES (V5.09)

A portable, public domain, version of the Data Encryption Standard.

Written with Symantec's THINK (Lightspeed) C by Richard Outerbridge.
Thanks to: Dan Hoey for his excellent Initial and Inverse permutation
code;  Jim Gillogly & Phil Karn for the DES key schedule code; Dennis
Ferguson, Eric Young and Dana How for comparing notes; and Ray Lau,
for humouring me on.

Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge.
(GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992.

Constant Summary collapse

BLOCK_SIZE =
8
BYTEBIT =
[
	01, 02, 04, 010, 020, 040, 0100, 0200
]
BIGBYTE =
[
	0x800000, 0x400000, 0x200000, 0x100000,
	0x080000, 0x040000, 0x020000, 0x010000,
	0x008000, 0x004000, 0x002000, 0x001000,
	0x000800, 0x000400, 0x000200, 0x000100,
	0x000080, 0x000040, 0x000020, 0x000010,
	0x000008, 0x000004, 0x000002, 0x000001
]
PC1 =

Use the key schedule specified in the Standard (ANSI X3.92-1981).

[
	56, 48, 40, 32, 24, 16,  8,	 0, 57, 49, 41, 33, 25, 17,
	 9,  1, 58, 50, 42, 34, 26,	18, 10,  2, 59, 51, 43, 35,
	62, 54, 46, 38, 30, 22, 14,	 6, 61, 53, 45, 37, 29, 21,
	13,  5, 60, 52, 44, 36, 28,	20, 12,  4, 27, 19, 11,  3 
]
TOTROT =
[
	1, 2, 4, 6, 8, 10, 12, 14, 15, 17, 19, 21, 23, 25, 27, 28
]
PC2 =
[
	13, 16, 10, 23,  0,  4,  2, 27, 14,  5, 20,  9,
	22, 18, 11,  3, 25,  7, 15,  6, 26, 19, 12,  1,
	40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47,
	43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31
]

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(key, mode) ⇒ DES

Create a des cipher object. key should be cipher key to use, and mode should be either :encrypt or :decrypt.

It will expand key to be 8 bytes by padding with null bytes. If it is longer than 8 bytes, the additional data is discarded.



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/cipher/des.rb', line 64

def initialize key, mode
	unless [:encrypt, :decrypt].include? mode
		raise ArgumentError, 'invalid mode argument - %s' % mode
	end
	@mode = mode

	# ensure key is 8 bytes. pad with nulls as needed
	key = key[0, BLOCK_SIZE]
	key << 0.chr * (BLOCK_SIZE - key.length)
	@key = key

	# now expand the key schedule
	@keys = self.class.send :prepare_key_stage2, self.class.send(:prepare_key_stage1, key, mode)

	# this internal buffer is used because we must process data in chunks of 8 bytes
	@buf = ''
end

Instance Attribute Details

#keyObject (readonly)

Returns the value of attribute key.



57
58
59
# File 'lib/cipher/des.rb', line 57

def key
  @key
end

#modeObject (readonly)

Returns the value of attribute mode.



57
58
59
# File 'lib/cipher/des.rb', line 57

def mode
  @mode
end

Class Method Details

.decrypt(key, data) ⇒ Object

A shortcut method to create a cipher object using key, and fully decrypt data



114
115
116
117
# File 'lib/cipher/des.rb', line 114

def self.decrypt key, data
	des = new key, :decrypt
	des.update(data) << des.final
end

.encrypt(key, data) ⇒ Object

A shortcut method to create a cipher object using key, and fully encrypt data



108
109
110
111
# File 'lib/cipher/des.rb', line 108

def self.encrypt key, data
	des = new key, :encrypt
	des.update(data) << des.final
end

Instance Method Details

#finalObject

This flushes the internal buffer by padding it out with null bytes, and doing a final DES round. Note that this means the ciphered text is always padded out to a multiple of 8 bytes.



99
100
101
102
103
104
105
# File 'lib/cipher/des.rb', line 99

def final
	if @buf.empty?
		''
	else
		update 0.chr * (BLOCK_SIZE - @buf.length)
	end
end

#update(data) ⇒ Object

This updates the cipher with data, returning any available ciphered output. The data is processed in blocks of 8 bytes, so any residual is added to an internal buffer.



84
85
86
87
88
89
90
91
92
93
94
# File 'lib/cipher/des.rb', line 84

def update data
	result = ''
	data = @buf + data unless @buf.empty?
	num_blocks, residual = data.length.divmod BLOCK_SIZE
	num_blocks.times do |i|
		block = data[i * BLOCK_SIZE, BLOCK_SIZE].unpack('N2')
		result << self.class.send(:desfunc, block, @keys).pack('N2')
	end
	@buf = residual == 0 ? '' : data[-residual..-1]
	result
end