Class: ManifestV3

Inherits:
Object
  • Object
show all
Defined in:
lib/ec2/amitools/manifestv3.rb

Overview

Manifest Version 3. Not backwards compatible

Defined Under Namespace

Classes: PartInformation

Constant Summary collapse

VERSION_STRING =
'3'
VERSION =
VERSION_STRING.to_i

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(xml = nil) ⇒ ManifestV3

Returns a new instance of ManifestV3.



34
35
36
37
38
39
40
41
42
# File 'lib/ec2/amitools/manifestv3.rb', line 34

def initialize( xml = nil )
  if xml == nil
    @doc = REXML::Document.new
  else
    # Convert to string if necessary.
    xml = ( xml.kind_of?( IO ) ? xml.read : xml )
    @doc = REXML::Document.new( xml )
  end
end

Class Method Details

.version3?(xml) ⇒ Boolean

Returns:

  • (Boolean)


178
179
180
181
182
# File 'lib/ec2/amitools/manifestv3.rb', line 178

def ManifestV3::version3?( xml )
  doc = REXML::Document.new( xml )
  version = REXML::XPath.first( doc.root, 'version' )
  return (version and version.text and version.text.to_i == VERSION)
end

Instance Method Details

#ami_part_info_listObject

Retrieve a list of AMI bundle parts info. Each element is a hash with the following elements:

  • ‘digest’

  • ‘filename’

  • ‘index’



228
229
230
231
232
233
234
235
236
237
# File 'lib/ec2/amitools/manifestv3.rb', line 228

def ami_part_info_list
  parts = Array.new
  REXML::XPath.each( @doc.root,'image/parts/part' ) do |part|
    index = part.attribute( 'index' ).to_s.to_i
    filename = REXML::XPath.first( part, 'filename' ).text
    digest = REXML::XPath.first( part, 'digest' ).text
    parts << { 'digest'=>digest, 'filename'=>filename, 'index'=>index }
  end
  return parts
end

#authenticate(cert) ⇒ Object

Verify the signature



300
301
302
303
304
# File 'lib/ec2/amitools/manifestv3.rb', line 300

def authenticate(cert)
  image_xml = XMLUtil.get_xml( @doc.to_s, 'image' )
  pubkey = Crypto::cert2pubkey(cert)
  Crypto::authenticate(image_xml, Format::hex2bin(signature), pubkey)
end

#bundled_sizeObject

Return the bundled size of the AMI.



257
258
259
# File 'lib/ec2/amitools/manifestv3.rb', line 257

def bundled_size()
  return get_element_text( 'image/bundled_size' ).to_i
end

#bundler_nameObject

Return the bundler name.



262
263
264
# File 'lib/ec2/amitools/manifestv3.rb', line 262

def bundler_name()
  return get_element_text('bundler/name')
end

#bundler_releaseObject

Return the bundler release.



272
273
274
# File 'lib/ec2/amitools/manifestv3.rb', line 272

def bundler_release()
  return get_element_text('bundler/release')
end

#bundler_versionObject

Return the bundler version.



267
268
269
# File 'lib/ec2/amitools/manifestv3.rb', line 267

def bundler_version()
  return get_element_text('bundler/version')
end

#cipher_algorithmObject

Get cipher algorithm used.



219
220
221
# File 'lib/ec2/amitools/manifestv3.rb', line 219

def cipher_algorithm()
  return REXML::XPath.first(@doc.root, 'image/ec2_encrypted_key/@algorithm').to_s
end

#digestObject

Return the AMI’s digest hex encoded.



185
186
187
# File 'lib/ec2/amitools/manifestv3.rb', line 185

def digest()
  return get_element_text( 'image/digest' )
end

#digest_algorithmObject

Get digest algorithm used.



214
215
216
# File 'lib/ec2/amitools/manifestv3.rb', line 214

def digest_algorithm()
  return REXML::XPath.first(@doc.root, 'image/digest/@algorithm').to_s
end

#docObject

for debugging only



45
46
47
# File 'lib/ec2/amitools/manifestv3.rb', line 45

def doc
  @doc
end

#ec2_encrypted_ivObject

The ec2 encrypted initialization vector hex encoded.



204
205
206
# File 'lib/ec2/amitools/manifestv3.rb', line 204

def ec2_encrypted_iv()
  return get_element_text( 'image/ec2_encrypted_iv' )
end

#ec2_encrypted_keyObject

The ec2 encrypted key hex encoded.



194
195
196
# File 'lib/ec2/amitools/manifestv3.rb', line 194

def ec2_encrypted_key()
  return get_element_text('image/ec2_encrypted_key' )
end

#init(name, user, parts, size, bundled_size, user_encrypted_key, ec2_encrypted_key, cipher_algorithm, user_encrypted_iv, ec2_encrypted_iv, digest, digest_algorithm, privkey_filename, bundler_name = nil, bundler_version = nil, bundler_release = nil) ⇒ Object

Initialize the manifest with AMI information. Return true if the initialization was succesful. Raise an exception on error.

Raises:

  • (ArgumentError)


52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/ec2/amitools/manifestv3.rb', line 52

def init(
  name,
  user,                   # The user's account number.
  parts,                  # A list of parts filenames and digest pairs.
  size,                   # The size of the AMI in bytes.
  bundled_size,           # The size of the bunled AMI in bytes.
  user_encrypted_key,     # Hex encoded.
  ec2_encrypted_key,      # Hex encoded.
  cipher_algorithm,       # The cipher algorithm used to encrypted the AMI.
  user_encrypted_iv,      # Hex encoded.
  ec2_encrypted_iv,       # Hex encoded.
  digest,                 # Hex encoded.
  digest_algorithm,       # The digest algorithm.
  privkey_filename,       # The user's private key filename.
  bundler_name = nil,
  bundler_version = nil,
  bundler_release = nil )
  # Check non-String parameter types.
  raise ArgumentError.new( "parts parameter type invalid" ) unless parts.is_a? Array
      
  # XML document.
  @doc = REXML::Document.new
  @doc << REXML::XMLDecl.new

  # manifest - the root element.
  manifest = REXML::Element.new( 'manifest' )

  @doc.add_element( manifest )
  
  # version - indicate the manifest version.
  version = REXML::Element.new( 'version' )
  version.text = VERSION_STRING
  manifest.add_element( version )
  
  # bundler information
  if bundler_name or bundler_version or bundler_release
    bundler_element = REXML::Element.new( 'bundler' )
    manifest.add_element( bundler_element )
    
    [['name', bundler_name ], 
     ['version', bundler_version ],
     ['release', bundler_release ]].each do |element_name, text|
      if element_name
        element = REXML::Element.new( element_name )
        element.text = text
        bundler_element.add_element( element )
      end
    end 
  end
  
  # image - the image element.
  image = REXML::Element.new( 'image' )
  name_element = REXML::Element.new( 'name' )
  name_element.text = name
  image.add_element( name_element )
  manifest.add_element( image )
  
  # user - the user's AWS access key ID.
  user_element = REXML::Element.new( 'user' )
  user_element.text = user
  image.add_element( user_element )
  
  # digest - the digest of the AMI.
  digest_element = REXML::Element.new( 'digest' )
  digest_element.add_attribute( 'algorithm', digest_algorithm )
  digest_element.add_text( digest )
  image.add_element( digest_element )
  
  # size - the size of the uncompressed AMI.
  size_element = REXML::Element.new( 'size' )
  size_element.text = size.to_s
  image.add_element( size_element )
  
  # size - the size of the uncompressed AMI.
  bundled_size_element = REXML::Element.new( 'bundled_size' )
  bundled_size_element.text = bundled_size.to_s
  image.add_element( bundled_size_element )
  
  # ec2 encrypted key element.
  ec2_encrypted_key_element = REXML::Element.new( 'ec2_encrypted_key' )
  ec2_encrypted_key_element.add_attribute( 'algorithm', cipher_algorithm )
  ec2_encrypted_key_element.add_text( ec2_encrypted_key )
  image.add_element( ec2_encrypted_key_element )
  
  # user encrypted key element.
  user_encrypted_key_element = REXML::Element.new( 'user_encrypted_key' )
  user_encrypted_key_element.add_attribute( 'algorithm', cipher_algorithm )
  user_encrypted_key_element.add_text( user_encrypted_key )
  image.add_element( user_encrypted_key_element )
  
  # ec2 encrypted iv element.
  ec2_encrypted_iv_element = REXML::Element.new( 'ec2_encrypted_iv' )
  ec2_encrypted_iv_element.add_text( ec2_encrypted_iv )
  image.add_element( ec2_encrypted_iv_element )
  
  # user encrypted iv element.
  user_encrypted_iv_element = REXML::Element.new( 'user_encrypted_iv' )
  user_encrypted_iv_element.add_text( user_encrypted_iv )
  image.add_element( user_encrypted_iv_element )
  
  # parts - list of the image parts.
  parts_element = REXML::Element.new( 'parts' )
  parts_element.add_attributes( {'count' => parts.size.to_s} )
  index=0
  parts.each do |part|
    # Add image part element for each image part.
    part_element = REXML::Element.new( 'part' )
    part_element.add_attribute( 'index', index.to_s )
    filename = REXML::Element.new( 'filename' )
    filename.add_text( part[0] )
    part_element.add_element( filename )
    digest = REXML::Element.new( 'digest' )
    digest.add_attribute( 'algorithm', digest_algorithm )
    digest.add_text( Format::bin2hex( part[1] ) )
    part_element.add_element( digest )
    parts_element.add_element( part_element )
    index+=1
  end
  image.add_element( parts_element )

  # Sign the manifest.
  sign( privkey_filename )
  
  return true
end

#nameObject



189
190
191
# File 'lib/ec2/amitools/manifestv3.rb', line 189

def name()
  return get_element_text( 'image/name' )
end

#partsObject

A list of PartInformation instances representing the AMI parts.



240
241
242
243
244
245
246
247
248
249
# File 'lib/ec2/amitools/manifestv3.rb', line 240

def parts()
  parts = []
  REXML::XPath.each( @doc.root,'image/parts/part' ) do |part|
    index = part.attribute( 'index' ).to_s.to_i
    filename = REXML::XPath.first( part, 'filename' ).text
    digest = Format::hex2bin(  REXML::XPath.first( part, 'digest' ).text  )
    parts[index] = PartInformation.new(  filename, digest  )
  end
  return parts
end

#sign(privkey_filename) ⇒ Object

Sign the manifest. If it is already signed, the signature and certificate will be replaced



278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
# File 'lib/ec2/amitools/manifestv3.rb', line 278

def sign( privkey_filename )
  unless privkey_filename.kind_of? String and File::exist?( privkey_filename )
    raise ArgumentError.new( "privkey_filename parameter invalid" )
  end
  
  # Get the XML for image element and sign it.
  image_xml = XMLUtil.get_xml( @doc.to_s, 'image' )
  sig = Crypto::sign( image_xml, privkey_filename )
       
  # Create the signature and certificate elements.
  signature = REXML::Element.new( 'signature' )
  signature.add_text( Format::bin2hex( sig ) )
  @doc.root.delete_element( 'signature' )
  @doc.root.add_element( signature )
end

#signatureObject

Return the signature



295
296
297
# File 'lib/ec2/amitools/manifestv3.rb', line 295

def signature
  get_element_text('signature')
end

#sizeObject

Return the size of the AMI.



252
253
254
# File 'lib/ec2/amitools/manifestv3.rb', line 252

def size()
  return get_element_text( 'image/size' ).to_i()
end

#to_sObject

Return the manifest as an XML string.



307
308
309
# File 'lib/ec2/amitools/manifestv3.rb', line 307

def to_s()
  return @doc.to_s
end

#userObject



311
312
313
# File 'lib/ec2/amitools/manifestv3.rb', line 311

def user()
  return get_element_text( 'image/user' )
end

#user_encrypted_ivObject

The user encrypted initialization vector hex encoded.



209
210
211
# File 'lib/ec2/amitools/manifestv3.rb', line 209

def user_encrypted_iv()
  return get_element_text( 'image/user_encrypted_iv' )
end

#user_encrypted_keyObject

The user encrypted key hex encoded.



199
200
201
# File 'lib/ec2/amitools/manifestv3.rb', line 199

def user_encrypted_key()
  return get_element_text( 'image/user_encrypted_key' )
end

#versionObject



315
316
317
# File 'lib/ec2/amitools/manifestv3.rb', line 315

def version()
  return get_element_text( 'version' ).to_i
end