ec2_amitools 1.0.2
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.
- 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
|