ec2_amitools 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +54 -0
- data/bin/console +14 -0
- data/bin/ec2-ami-tools-version +6 -0
- data/bin/ec2-bundle-image +6 -0
- data/bin/ec2-bundle-vol +6 -0
- data/bin/ec2-delete-bundle +6 -0
- data/bin/ec2-download-bundle +6 -0
- data/bin/ec2-migrate-bundle +6 -0
- data/bin/ec2-migrate-manifest +6 -0
- data/bin/ec2-unbundle +6 -0
- data/bin/ec2-upload-bundle +6 -0
- data/bin/setup +8 -0
- data/etc/ec2/amitools/cert-ec2-cn-north-1.pem +28 -0
- data/etc/ec2/amitools/cert-ec2-gov.pem +17 -0
- data/etc/ec2/amitools/cert-ec2.pem +23 -0
- data/etc/ec2/amitools/mappings.csv +9 -0
- data/lib/ec2/amitools/bundle.rb +251 -0
- data/lib/ec2/amitools/bundle_base.rb +58 -0
- data/lib/ec2/amitools/bundleimage.rb +94 -0
- data/lib/ec2/amitools/bundleimageparameters.rb +42 -0
- data/lib/ec2/amitools/bundlemachineparameters.rb +60 -0
- data/lib/ec2/amitools/bundleparameters.rb +120 -0
- data/lib/ec2/amitools/bundlevol.rb +240 -0
- data/lib/ec2/amitools/bundlevolparameters.rb +164 -0
- data/lib/ec2/amitools/crypto.rb +379 -0
- data/lib/ec2/amitools/decryptmanifest.rb +20 -0
- data/lib/ec2/amitools/defaults.rb +12 -0
- data/lib/ec2/amitools/deletebundle.rb +212 -0
- data/lib/ec2/amitools/deletebundleparameters.rb +78 -0
- data/lib/ec2/amitools/downloadbundle.rb +161 -0
- data/lib/ec2/amitools/downloadbundleparameters.rb +84 -0
- data/lib/ec2/amitools/exception.rb +86 -0
- data/lib/ec2/amitools/fileutil.rb +219 -0
- data/lib/ec2/amitools/format.rb +127 -0
- data/lib/ec2/amitools/instance-data.rb +97 -0
- data/lib/ec2/amitools/manifest_wrapper.rb +132 -0
- data/lib/ec2/amitools/manifestv20070829.rb +361 -0
- data/lib/ec2/amitools/manifestv20071010.rb +403 -0
- data/lib/ec2/amitools/manifestv3.rb +331 -0
- data/lib/ec2/amitools/mapids.rb +148 -0
- data/lib/ec2/amitools/migratebundle.rb +222 -0
- data/lib/ec2/amitools/migratebundleparameters.rb +173 -0
- data/lib/ec2/amitools/migratemanifest.rb +225 -0
- data/lib/ec2/amitools/migratemanifestparameters.rb +118 -0
- data/lib/ec2/amitools/minimalec2.rb +116 -0
- data/lib/ec2/amitools/parameter_exceptions.rb +34 -0
- data/lib/ec2/amitools/parameters_base.rb +168 -0
- data/lib/ec2/amitools/region.rb +93 -0
- data/lib/ec2/amitools/s3toolparameters.rb +183 -0
- data/lib/ec2/amitools/showversion.rb +12 -0
- data/lib/ec2/amitools/syschecks.rb +27 -0
- data/lib/ec2/amitools/tool_base.rb +224 -0
- data/lib/ec2/amitools/unbundle.rb +107 -0
- data/lib/ec2/amitools/unbundleparameters.rb +65 -0
- data/lib/ec2/amitools/uploadbundle.rb +361 -0
- data/lib/ec2/amitools/uploadbundleparameters.rb +108 -0
- data/lib/ec2/amitools/util.rb +532 -0
- data/lib/ec2/amitools/version.rb +33 -0
- data/lib/ec2/amitools/xmlbuilder.rb +237 -0
- data/lib/ec2/amitools/xmlutil.rb +55 -0
- data/lib/ec2/common/constants.rb +16 -0
- data/lib/ec2/common/curl.rb +110 -0
- data/lib/ec2/common/headers.rb +95 -0
- data/lib/ec2/common/headersv4.rb +173 -0
- data/lib/ec2/common/http.rb +333 -0
- data/lib/ec2/common/s3support.rb +231 -0
- data/lib/ec2/common/signature.rb +68 -0
- data/lib/ec2/oem/LICENSE.txt +58 -0
- data/lib/ec2/oem/open4.rb +399 -0
- data/lib/ec2/platform/base/architecture.rb +26 -0
- data/lib/ec2/platform/base/constants.rb +54 -0
- data/lib/ec2/platform/base/pipeline.rb +181 -0
- data/lib/ec2/platform/base.rb +57 -0
- data/lib/ec2/platform/current.rb +55 -0
- data/lib/ec2/platform/linux/architecture.rb +35 -0
- data/lib/ec2/platform/linux/constants.rb +23 -0
- data/lib/ec2/platform/linux/fstab.rb +99 -0
- data/lib/ec2/platform/linux/identity.rb +16 -0
- data/lib/ec2/platform/linux/image.rb +811 -0
- data/lib/ec2/platform/linux/mtab.rb +74 -0
- data/lib/ec2/platform/linux/pipeline.rb +40 -0
- data/lib/ec2/platform/linux/rsync.rb +114 -0
- data/lib/ec2/platform/linux/tar.rb +124 -0
- data/lib/ec2/platform/linux/uname.rb +50 -0
- data/lib/ec2/platform/linux.rb +83 -0
- data/lib/ec2/platform/solaris/architecture.rb +28 -0
- data/lib/ec2/platform/solaris/constants.rb +30 -0
- data/lib/ec2/platform/solaris/fstab.rb +43 -0
- data/lib/ec2/platform/solaris/identity.rb +16 -0
- data/lib/ec2/platform/solaris/image.rb +327 -0
- data/lib/ec2/platform/solaris/mtab.rb +29 -0
- data/lib/ec2/platform/solaris/pipeline.rb +40 -0
- data/lib/ec2/platform/solaris/rsync.rb +24 -0
- data/lib/ec2/platform/solaris/tar.rb +36 -0
- data/lib/ec2/platform/solaris/uname.rb +21 -0
- data/lib/ec2/platform/solaris.rb +38 -0
- data/lib/ec2/platform.rb +69 -0
- data/lib/ec2/version.rb +8 -0
- data/lib/ec2_amitools +1 -0
- data/lib/ec2_amitools.rb +7 -0
- metadata +184 -0
@@ -0,0 +1,164 @@
|
|
1
|
+
# Copyright 2008-2014 Amazon.com, Inc. or its affiliates. All Rights
|
2
|
+
# Reserved. Licensed under the Amazon Software License (the
|
3
|
+
# "License"). You may not use this file except in compliance with the
|
4
|
+
# License. A copy of the License is located at
|
5
|
+
# http://aws.amazon.com/asl or in the "license" file accompanying this
|
6
|
+
# file. This file is distributed on an "AS IS" BASIS, WITHOUT
|
7
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
8
|
+
# the License for the specific language governing permissions and
|
9
|
+
# limitations under the License.
|
10
|
+
|
11
|
+
require 'ec2/amitools/bundlemachineparameters'
|
12
|
+
|
13
|
+
# The Bundle Volume command line parameters.
|
14
|
+
class BundleVolParameters < BundleMachineParameters
|
15
|
+
|
16
|
+
PREFIX_DESCRIPTION = "The filename prefix for bundled AMI files. Defaults to \"image\"."
|
17
|
+
EXCLUDE_DESCRIPTION = ["A comma-separated list of absolute directory paths, relative to",
|
18
|
+
"the volume being bundled, to exclude. This option overrides the",
|
19
|
+
"\"--all\" option."]
|
20
|
+
INCLUDE_DESCRIPTION = ["Linux Only. A comma-separated list of absolute file paths, relative",
|
21
|
+
"to the volume being bundled, to include. This option overrides the",
|
22
|
+
"default filtered files list."]
|
23
|
+
FILTER_DESCRIPTION = "Do not use the default filtered files list."
|
24
|
+
CLONE_VOLUME_DESCRIPTION = 'Clone volume into an image but do not generate a bundle.'
|
25
|
+
ALL_DESCRIPTION = ["Include all directories in the volume being bundled, including those",
|
26
|
+
"on remotely mounted filesystems."]
|
27
|
+
SIZE_DESCRIPTION = ["The size, in MB (1024 * 1024 bytes), of the image file to create.",
|
28
|
+
"The maximum size is 10240 MB."]
|
29
|
+
VOLUME_DESCRIPTION = "The absolute path to the mounted volume to be bundled. Defaults to \"/\"."
|
30
|
+
FSTAB_DESCRIPTION = "The absolute path to the fstab to be bundled into the image."
|
31
|
+
GENERATE_FSTAB_DESCRIPTION= ["Inject a generated EC2 fstab. (Only use this if you are not rebundling",
|
32
|
+
"an existing instance.)"]
|
33
|
+
GRUB_CONFIG_DESCRIPTION = "The absolute path to the grub config to be bundled into the image."
|
34
|
+
PARTITION_TYPE_DESCRIPTION= ["Create a disk image using a partition table. Optionally, specify the",
|
35
|
+
"partition table type to use. Viable options are 'mbr' or 'gpt'. If no",
|
36
|
+
"partition table type is specified, the type used on the parent block",
|
37
|
+
"device of the specified volume is used, if applicable, otherwise, we",
|
38
|
+
"default to using 'gpt'. You may also specify 'none' to disable the use",
|
39
|
+
"of partition tables, altogether." ]
|
40
|
+
SCRIPT_DESCRIPTION = ['Executable customization script to call after cloning volume. The file',
|
41
|
+
'must expect a single argument: the mount point of the cloned volume.']
|
42
|
+
INHERIT_DESCRIPTION = ['Inherit instance metadata. Enabled by default.',
|
43
|
+
'Bundling will fail if inherit is enabled but instance data',
|
44
|
+
'is not accessible, for example not bundling an EC2 instance.']
|
45
|
+
|
46
|
+
attr_accessor :all,
|
47
|
+
:exclude,
|
48
|
+
:includes,
|
49
|
+
:filter,
|
50
|
+
:prefix,
|
51
|
+
:size,
|
52
|
+
:volume,
|
53
|
+
:fstab,
|
54
|
+
:part_type,
|
55
|
+
:inherit,
|
56
|
+
:clone_only,
|
57
|
+
:script,
|
58
|
+
:generate_fstab,
|
59
|
+
:grub_config
|
60
|
+
|
61
|
+
def optional_params()
|
62
|
+
super()
|
63
|
+
on('-a', '--all', *ALL_DESCRIPTION) do
|
64
|
+
@all = true
|
65
|
+
end
|
66
|
+
|
67
|
+
on('-e', '--exclude DIR1,DIR2,...', Array, *EXCLUDE_DESCRIPTION) do |p|
|
68
|
+
@exclude = p
|
69
|
+
end
|
70
|
+
|
71
|
+
on('-i', '--include FILE1,FILE2,...', Array, *INCLUDE_DESCRIPTION) do |p|
|
72
|
+
@includes = p
|
73
|
+
end
|
74
|
+
|
75
|
+
on('--no-filter', FILTER_DESCRIPTION) do
|
76
|
+
@filter = false
|
77
|
+
end
|
78
|
+
|
79
|
+
on('-p', '--prefix PREFIX', String, PREFIX_DESCRIPTION) do |prefix|
|
80
|
+
assert_good_key(prefix, '--prefix')
|
81
|
+
@prefix = prefix
|
82
|
+
end
|
83
|
+
|
84
|
+
on('--clone-only', CLONE_VOLUME_DESCRIPTION) do
|
85
|
+
@clone_only = true
|
86
|
+
end
|
87
|
+
|
88
|
+
on('-s', '--size MB', Integer, *SIZE_DESCRIPTION) do |p|
|
89
|
+
@size = p
|
90
|
+
end
|
91
|
+
|
92
|
+
on('--[no-]inherit', *INHERIT_DESCRIPTION) do |p|
|
93
|
+
@inherit = p
|
94
|
+
end
|
95
|
+
|
96
|
+
on('-v', '--volume PATH', String, VOLUME_DESCRIPTION) do |volume|
|
97
|
+
assert_directory_exists(volume, '--volume')
|
98
|
+
@volume = volume
|
99
|
+
end
|
100
|
+
|
101
|
+
on('--fstab PATH', String, FSTAB_DESCRIPTION) do |fstab|
|
102
|
+
assert_file_exists(fstab, '--fstab')
|
103
|
+
@fstab = fstab
|
104
|
+
end
|
105
|
+
|
106
|
+
on('--grub-config PATH', String, GRUB_CONFIG_DESCRIPTION) do |grub_config|
|
107
|
+
assert_file_exists(grub_config, '--grub-config')
|
108
|
+
@grub_config = grub_config
|
109
|
+
end
|
110
|
+
|
111
|
+
on('-P', '--partition TYPE', EC2::Platform::PartitionType.list,
|
112
|
+
*PARTITION_TYPE_DESCRIPTION) do |ptype|
|
113
|
+
@part_type = ptype
|
114
|
+
end
|
115
|
+
|
116
|
+
on('-S', '--script SCRIPT', String, *SCRIPT_DESCRIPTION) do |scriptfile|
|
117
|
+
assert_file_executable(scriptfile, '--script')
|
118
|
+
@script = scriptfile
|
119
|
+
end
|
120
|
+
|
121
|
+
on('--generate-fstab', *GENERATE_FSTAB_DESCRIPTION) do
|
122
|
+
@generate_fstab = true
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def validate_params()
|
127
|
+
raise InvalidCombination.new("--fstab", "--generate-fstab") if @fstab and @generate_fstab
|
128
|
+
|
129
|
+
if @exclude
|
130
|
+
volume = @volume || '/'
|
131
|
+
@exclude.each do |dir|
|
132
|
+
path = File::join(volume, dir)
|
133
|
+
assert_glob_expands(path, '--exclude')
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
if @includes
|
138
|
+
volume = @volume || '/'
|
139
|
+
@includes.each do |file|
|
140
|
+
path = File::join(volume, file)
|
141
|
+
assert_glob_expands(path, '--include')
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
super()
|
146
|
+
end
|
147
|
+
|
148
|
+
def set_defaults()
|
149
|
+
super()
|
150
|
+
@inherit = true if @inherit.nil? # false means a parameter was provided.
|
151
|
+
@exclude ||= []
|
152
|
+
@includes ||= []
|
153
|
+
@filter = true if @filter.nil?
|
154
|
+
@prefix ||= 'image'
|
155
|
+
@size ||= MAX_SIZE_MB
|
156
|
+
@volume ||= '/'
|
157
|
+
@clone_only ||= false
|
158
|
+
@part_type ||= :auto
|
159
|
+
if @generate_fstab
|
160
|
+
@fstab = :default
|
161
|
+
@fstab = :legacy if @arch == "i386"
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
@@ -0,0 +1,379 @@
|
|
1
|
+
# Copyright 2008-2014 Amazon.com, Inc. or its affiliates. All Rights
|
2
|
+
# Reserved. Licensed under the Amazon Software License (the
|
3
|
+
# "License"). You may not use this file except in compliance with the
|
4
|
+
# License. A copy of the License is located at
|
5
|
+
# http://aws.amazon.com/asl or in the "license" file accompanying this
|
6
|
+
# file. This file is distributed on an "AS IS" BASIS, WITHOUT
|
7
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
8
|
+
# the License for the specific language governing permissions and
|
9
|
+
# limitations under the License.
|
10
|
+
|
11
|
+
require 'ec2/amitools/format'
|
12
|
+
require 'digest/sha1'
|
13
|
+
require 'openssl'
|
14
|
+
require 'stringio'
|
15
|
+
|
16
|
+
###
|
17
|
+
# Cryptographic utilities module.
|
18
|
+
#
|
19
|
+
module Crypto
|
20
|
+
BUFFER_SIZE = 1024 * 1024
|
21
|
+
ASYM_ALG = 'RSA'
|
22
|
+
SYM_ALG = 'AES-128-CBC'
|
23
|
+
DIGEST_ALG = 'SHA1'
|
24
|
+
PADDING = OpenSSL::PKey::RSA::PKCS1_PADDING
|
25
|
+
VERSION1 = 1
|
26
|
+
VERSION2 = 2
|
27
|
+
SHA1_FINGERPRINT_REGEX = /([a-f0-9]{2}(:[a-f0-9]{2}){15})/i
|
28
|
+
|
29
|
+
#----------------------------------------------------------------------------#
|
30
|
+
|
31
|
+
##
|
32
|
+
# Decrypt the specified cipher text according to the AMI Manifest Encryption
|
33
|
+
# Scheme Version 1 or 2.
|
34
|
+
#
|
35
|
+
# ((|cipher_text|)) The cipher text to decrypt.
|
36
|
+
# ((|keyio_or_keyfilename|)) The key data IO stream or the name of the private
|
37
|
+
# key file.
|
38
|
+
#
|
39
|
+
def Crypto.decryptasym(cipher_text, keyfilename)
|
40
|
+
raise ArgumentError.new('cipher_text') unless cipher_text
|
41
|
+
raise ArgumentError.new('keyfilename') unless keyfilename and FileTest.exists? keyfilename
|
42
|
+
|
43
|
+
# Load key.
|
44
|
+
privkey = File.open(keyfilename, 'r') { |f| OpenSSL::PKey::RSA.new(f) }
|
45
|
+
|
46
|
+
# Get version.
|
47
|
+
version = cipher_text[0]
|
48
|
+
if version == VERSION2
|
49
|
+
return Crypto.decryptasym_v2( cipher_text, keyfilename )
|
50
|
+
end
|
51
|
+
raise ArgumentError.new("invalid encryption scheme versionb: #{version}") unless version == 1
|
52
|
+
|
53
|
+
# Decrypt and extract encrypted symmetric key and initialization vector.
|
54
|
+
symkey_cryptogram_len = cipher_text.slice(1, 2).unpack('C')[0]
|
55
|
+
symkey_cryptogram = privkey.private_decrypt(
|
56
|
+
cipher_text.slice(2, symkey_cryptogram_len),
|
57
|
+
PADDING)
|
58
|
+
symkey = symkey_cryptogram.slice(0, 16)
|
59
|
+
iv = symkey_cryptogram.slice(16, 16)
|
60
|
+
|
61
|
+
# Decrypt data with the symmetric key.
|
62
|
+
cryptogram = cipher_text.slice(2 + symkey_cryptogram_len..cipher_text.size)
|
63
|
+
decryptsym(cryptogram, symkey, iv)
|
64
|
+
end
|
65
|
+
|
66
|
+
#----------------------------------------------------------------------------#
|
67
|
+
|
68
|
+
##
|
69
|
+
# Decrypt the specified cipher text according to the AMI Manifest Encryption
|
70
|
+
# Scheme Version 2.
|
71
|
+
#
|
72
|
+
# ((|cipher_text|)) The cipher text to decrypt.
|
73
|
+
# ((|keyio_or_keyfilename|)) The key data IO stream or the name of the private
|
74
|
+
# key file.
|
75
|
+
#
|
76
|
+
def Crypto.decryptasym_v2(cipher_text, keyfilename)
|
77
|
+
raise ArgumentError.new('cipher_text') unless cipher_text
|
78
|
+
raise ArgumentError.new('keyfilename') unless keyfilename and FileTest.exists? keyfilename
|
79
|
+
|
80
|
+
# Load key.
|
81
|
+
privkey = File.open(keyfilename, 'r') { |f| OpenSSL::PKey::RSA.new(f) }
|
82
|
+
|
83
|
+
# Get version.
|
84
|
+
version = cipher_text[0]
|
85
|
+
raise ArgumentError.new("invalid encryption scheme versionb: #{version}") unless version == VERSION2
|
86
|
+
|
87
|
+
# Decrypt and extract encrypted symmetric key and initialization vector.
|
88
|
+
hi_byte, lo_byte = cipher_text.slice(1, 3).unpack('CC')
|
89
|
+
symkey_cryptogram_len = ( hi_byte << 8 ) | lo_byte
|
90
|
+
symkey_cryptogram = privkey.private_decrypt(
|
91
|
+
cipher_text.slice(3, symkey_cryptogram_len),
|
92
|
+
PADDING)
|
93
|
+
|
94
|
+
symkey = symkey_cryptogram.slice(0, 16)
|
95
|
+
iv = symkey_cryptogram.slice(16, 16)
|
96
|
+
|
97
|
+
# Decrypt data with the symmetric key.
|
98
|
+
cryptogram = cipher_text.slice( ( 3 + symkey_cryptogram_len )..cipher_text.size)
|
99
|
+
decryptsym(cryptogram, symkey, iv)
|
100
|
+
end
|
101
|
+
|
102
|
+
#----------------------------------------------------------------------------#
|
103
|
+
|
104
|
+
##
|
105
|
+
# Asymmetrically encrypt the specified data using the AMI Manifest Encryption
|
106
|
+
# Scheme Version 2.
|
107
|
+
#
|
108
|
+
# The data is encrypted with an ephemeral symmetric key and initialization
|
109
|
+
# vector. The symmetric key and initialization vector are encrypted with the
|
110
|
+
# specified public key and preprended to the data.
|
111
|
+
#
|
112
|
+
# ((|data|)) The data to encrypt.
|
113
|
+
# ((|pubkey|)) The public key.
|
114
|
+
#
|
115
|
+
def Crypto.encryptasym(data, pubkey)
|
116
|
+
raise ArgumentError.new('data') unless data
|
117
|
+
raise ArgumentError.new('pubkey') unless pubkey
|
118
|
+
|
119
|
+
symkey = gensymkey
|
120
|
+
iv = geniv
|
121
|
+
symkey_cryptogram = pubkey.public_encrypt( symkey + iv, PADDING )
|
122
|
+
|
123
|
+
data_cryptogram = encryptsym(data, symkey, iv)
|
124
|
+
|
125
|
+
hi_byte, lo_byte = Format.int2int16(symkey_cryptogram.size)
|
126
|
+
|
127
|
+
Format::int2byte(VERSION2) + hi_byte + lo_byte + symkey_cryptogram + data_cryptogram
|
128
|
+
end
|
129
|
+
|
130
|
+
#----------------------------------------------------------------------------#
|
131
|
+
|
132
|
+
##
|
133
|
+
# Verify the authenticity of the data from the IO stream or string ((|data|))
|
134
|
+
# using the signature ((|sig|)) and the public key ((|pubkey|)).
|
135
|
+
#
|
136
|
+
# Return true iff the signature is valid.
|
137
|
+
#
|
138
|
+
def Crypto.authenticate(data, sig, pubkey)
|
139
|
+
raise ArgumentError.new("Invalid parameter data") if data.nil?
|
140
|
+
raise ArgumentError.new("Invalid parameter sig") if sig.nil? or sig.length==0
|
141
|
+
raise ArgumentError.new("Invalid parameter pubkey") if pubkey.nil?
|
142
|
+
|
143
|
+
# Create IO stream if necessary.
|
144
|
+
io = (data.instance_of?(StringIO) ? data : StringIO.new(data))
|
145
|
+
|
146
|
+
sha = OpenSSL::Digest::SHA1.new
|
147
|
+
res = false
|
148
|
+
while not (io.eof?)
|
149
|
+
res = pubkey.verify(sha, sig, io.read(BUFFER_SIZE))
|
150
|
+
end
|
151
|
+
res
|
152
|
+
end
|
153
|
+
|
154
|
+
#----------------------------------------------------------------------------#
|
155
|
+
|
156
|
+
##
|
157
|
+
# Decrypt the specified cipher text file to create the specified plain text
|
158
|
+
# file.
|
159
|
+
#
|
160
|
+
# The symmetric cipher is AES in CBC mode. 128 bit keys are used. If the plain
|
161
|
+
# text file already exists it will be overwritten.
|
162
|
+
#
|
163
|
+
# ((|src|)) The name of the cipher text file to decrypt.
|
164
|
+
# ((|dst|)) The name of the plain text file to create.
|
165
|
+
# ((|key|)) The 128 bit (16 byte) symmetric key.
|
166
|
+
# ((|iv|)) The 128 bit (16 byte) initialization vector.
|
167
|
+
#
|
168
|
+
def Crypto.decryptfile(src, dst, key, iv)
|
169
|
+
raise ArgumentError.new("invalid file name: #{src}") unless FileTest.exists?(src)
|
170
|
+
raise ArgumentError.new("invalid key") unless key and key.size == 16
|
171
|
+
raise ArgumentError.new("invalid iv") unless iv and iv.size == 16
|
172
|
+
pio = IO.popen( "openssl enc -d -aes-128-cbc -in #{src} -out #{dst} -K #{Format::bin2hex(key)} -iv #{Format::bin2hex(iv)} 2>&1" )
|
173
|
+
result = pio.read
|
174
|
+
pio.close
|
175
|
+
raise "error decrypting file #{src}: #{result}" if result.strip != ''
|
176
|
+
end
|
177
|
+
|
178
|
+
#----------------------------------------------------------------------------#
|
179
|
+
|
180
|
+
##
|
181
|
+
# Decrypt _ciphertext_ using _key_ and _iv_ using AES-128-CBC.
|
182
|
+
#
|
183
|
+
def Crypto.decryptsym(plaintext, key, iv)
|
184
|
+
raise ArgumentError.new("plaintext must be a String") unless plaintext.is_a? String
|
185
|
+
raise ArgumentError.new("invalid key") unless key.is_a? String and key.size == 16
|
186
|
+
raise ArgumentError.new("invalid iv") unless iv.is_a? String and iv.size == 16
|
187
|
+
|
188
|
+
cipher = OpenSSL::Cipher::Cipher.new( 'AES-128-CBC' )
|
189
|
+
cipher.decrypt( key, iv )
|
190
|
+
# NOTE: If the key and iv aren't set this doesn't work correctly.
|
191
|
+
cipher.key = key
|
192
|
+
cipher.iv = iv
|
193
|
+
plaintext = cipher.update( plaintext )
|
194
|
+
plaintext + cipher.final
|
195
|
+
end
|
196
|
+
|
197
|
+
#----------------------------------------------------------------------------#
|
198
|
+
|
199
|
+
##
|
200
|
+
# Generate and return a message digest for the data from the IO stream
|
201
|
+
# ((|io|)), using the algorithm alg
|
202
|
+
#
|
203
|
+
def Crypto.digest(io, alg = OpenSSL::Digest::SHA1.new)
|
204
|
+
raise ArgumentError.new('io') unless io.kind_of?(IO) or io.kind_of?(StringIO)
|
205
|
+
while not io.eof?
|
206
|
+
alg.update(io.read(BUFFER_SIZE))
|
207
|
+
end
|
208
|
+
alg.digest
|
209
|
+
end
|
210
|
+
|
211
|
+
#----------------------------------------------------------------------------#
|
212
|
+
|
213
|
+
# Return the HMAC SHA1 of _data_ using _key_.
|
214
|
+
def Crypto.hmac_sha1( key, data )
|
215
|
+
raise ParameterError.new( "key must be a String" ) unless key.is_a? String
|
216
|
+
raise ParameterError.new( "data must be a String" ) unless data.is_a? String
|
217
|
+
|
218
|
+
md = OpenSSL::Digest::SHA1.new
|
219
|
+
hmac = OpenSSL::HMAC.new( key, md)
|
220
|
+
hmac.update( data )
|
221
|
+
return hmac.digest
|
222
|
+
end
|
223
|
+
|
224
|
+
#----------------------------------------------------------------------------#
|
225
|
+
|
226
|
+
##
|
227
|
+
# Decrypt the specified cipher text file to create the specified plain text
|
228
|
+
# file.
|
229
|
+
#
|
230
|
+
# The symmetric cipher is AES in CBC mode. 128 bit keys are used. If the plain
|
231
|
+
# text file already exists it will be overwritten.
|
232
|
+
#
|
233
|
+
# ((|key|)) The 128 bit (16 byte) symmetric key.
|
234
|
+
# ((|src|)) The name of the cipher text file to encrypt.
|
235
|
+
# ((|dst|)) The name of the plain text file to create.
|
236
|
+
# ((|iv|)) The 128 bit (16 byte) initialization vector.
|
237
|
+
#
|
238
|
+
def Crypto.encryptfile(src, dst, key, iv)
|
239
|
+
raise ArgumentError.new("invalid file name: #{src}") unless FileTest.exists?(src)
|
240
|
+
raise ArgumentError.new("invalid key") unless key and key.size == 16
|
241
|
+
raise ArgumentError.new("invalid iv") unless iv and iv.size == 16
|
242
|
+
cmd = "openssl enc -e -aes-128-cbc -in #{src} -out #{dst} -K #{Format::bin2hex(key)} -iv #{Format::bin2hex(iv)}"
|
243
|
+
result = Kernel::system(cmd)
|
244
|
+
raise "error encrypting file #{src}" unless result
|
245
|
+
end
|
246
|
+
|
247
|
+
#----------------------------------------------------------------------------#
|
248
|
+
|
249
|
+
##
|
250
|
+
# Encrypt _plaintext_ with _key_ and _iv_ using AES-128-CBC.
|
251
|
+
#
|
252
|
+
def Crypto.encryptsym(plaintext, key, iv)
|
253
|
+
raise ArgumentError.new("plaintext must be a String") unless plaintext.kind_of? String
|
254
|
+
raise ArgumentError.new("invalid key") unless ( key.is_a? String and key.size == 16 )
|
255
|
+
raise ArgumentError.new("invalid iv") unless ( iv.is_a? String and iv.size == 16 )
|
256
|
+
|
257
|
+
cipher = OpenSSL::Cipher::Cipher.new( 'AES-128-CBC' )
|
258
|
+
cipher.encrypt( key, iv )
|
259
|
+
# NOTE: If the key and iv aren't set this doesn't work correctly.
|
260
|
+
cipher.key = key
|
261
|
+
cipher.iv = iv
|
262
|
+
ciphertext = cipher.update( plaintext )
|
263
|
+
ciphertext + cipher.final
|
264
|
+
end
|
265
|
+
|
266
|
+
#----------------------------------------------------------------------------#
|
267
|
+
|
268
|
+
##
|
269
|
+
# Generate an initialization vector suitable use with symmetric cipher.
|
270
|
+
#
|
271
|
+
def Crypto.geniv
|
272
|
+
OpenSSL::Cipher::Cipher.new(SYM_ALG).random_iv
|
273
|
+
end
|
274
|
+
|
275
|
+
#----------------------------------------------------------------------------#
|
276
|
+
|
277
|
+
##
|
278
|
+
# Generate a key suitable for use with a symmetric cipher.
|
279
|
+
#
|
280
|
+
def Crypto.gensymkey
|
281
|
+
OpenSSL::Cipher::Cipher.new(SYM_ALG).random_key
|
282
|
+
end
|
283
|
+
|
284
|
+
#----------------------------------------------------------------------------#
|
285
|
+
|
286
|
+
##
|
287
|
+
# Return the public key from the X509 certificate file ((|filename|)).
|
288
|
+
#
|
289
|
+
def Crypto.certfile2pubkey(filename)
|
290
|
+
begin
|
291
|
+
File.open(filename) do |f|
|
292
|
+
return cert2pubkey(f)
|
293
|
+
end
|
294
|
+
rescue Exception => e
|
295
|
+
raise "error reading certificate file #{filename}: #{e.message}"
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
#----------------------------------------------------------------------------#
|
300
|
+
|
301
|
+
def Crypto.cert2pubkey(data)
|
302
|
+
begin
|
303
|
+
return OpenSSL::X509::Certificate.new(data).public_key
|
304
|
+
rescue Exception => e
|
305
|
+
raise "error reading certificate: #{e.message}"
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
#----------------------------------------------------------------------------#
|
310
|
+
|
311
|
+
##
|
312
|
+
# Sign the data from IO stream or string ((|data|)) using the key in
|
313
|
+
# ((|keyfilename|)).
|
314
|
+
#
|
315
|
+
# Return the signature.
|
316
|
+
#
|
317
|
+
def Crypto.sign(data, keyfilename)
|
318
|
+
raise ArgumentError.new('data') unless data
|
319
|
+
raise ArgumentError.new("invalid file name: #{keyfilename}") unless FileTest.exists?(keyfilename)
|
320
|
+
|
321
|
+
# Create an IO stream from the data if necessary.
|
322
|
+
io = (data.instance_of?(StringIO) ? data : StringIO.new(data))
|
323
|
+
|
324
|
+
sha = OpenSSL::Digest::SHA1.new
|
325
|
+
pk = loadprivkey( keyfilename )
|
326
|
+
return pk.sign(sha, io.read )
|
327
|
+
end
|
328
|
+
|
329
|
+
#------------------------------------------------------------------------------#
|
330
|
+
|
331
|
+
##
|
332
|
+
# Generate the SHA1 fingerprint for a PEM-encoded certificate (NOT private key)
|
333
|
+
# Returns the fingerprint in aa:bb:... form
|
334
|
+
# Raises ArgumentError if the fingerprint cannot be obtained
|
335
|
+
#
|
336
|
+
def Crypto.cert_sha1_fingerprint(cert_filename)
|
337
|
+
raise ArgumentError.new('cert_filename is nil') if cert_filename.nil?
|
338
|
+
raise ArgumentError.new("invalid cert file name: #{cert_filename}") unless FileTest.exists?(cert_filename)
|
339
|
+
fingerprint = nil
|
340
|
+
|
341
|
+
IO.popen("openssl x509 -in #{cert_filename} -noout -sha1 -fingerprint") do |io|
|
342
|
+
out = io.read
|
343
|
+
md = SHA1_FINGERPRINT_REGEX.match(out)
|
344
|
+
if md
|
345
|
+
fingerprint = md[1]
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
raise ArgumentError.new("could not generate fingerprint for #{cert_filename}") if fingerprint.nil?
|
350
|
+
|
351
|
+
return fingerprint
|
352
|
+
end
|
353
|
+
|
354
|
+
#------------------------------------------------------------------------------#
|
355
|
+
|
356
|
+
def Crypto.loadprivkey filename
|
357
|
+
begin
|
358
|
+
OpenSSL::PKey::RSA.new( File.open( filename,'r' ) )
|
359
|
+
rescue Exception => e
|
360
|
+
raise "error reading private key from file #{filename}: #{e.message}"
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
#----------------------------------------------------------------------------#
|
365
|
+
|
366
|
+
##
|
367
|
+
# XOR the byte string ((|a|)) with the byte string ((|b|)). The operans must
|
368
|
+
# be of the same length.
|
369
|
+
#
|
370
|
+
def Crypto.xor(a, b)
|
371
|
+
raise ArgumentError.new('data lengths differ') unless a.size == b.size
|
372
|
+
xored = String.new
|
373
|
+
a.size.times do |i|
|
374
|
+
xored << (a[i] ^ b[i])
|
375
|
+
end
|
376
|
+
|
377
|
+
xored
|
378
|
+
end
|
379
|
+
end
|