Fixed some typos, courtesy of codespell
[strongswan.git] / src / libstrongswan / plugins / openssl / openssl_pkcs7.c
1 /*
2 * Copyright (C) 2012 Martin Willi
3 * Copyright (C) 2012 revosec AG
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include <openssl/opensslv.h>
17 #include <openssl/opensslconf.h>
18
19 #if OPENSSL_VERSION_NUMBER >= 0x0090807fL
20 #ifndef OPENSSL_NO_CMS
21
22 #include "openssl_pkcs7.h"
23 #include "openssl_util.h"
24
25 #include <library.h>
26 #include <utils/debug.h>
27 #include <asn1/oid.h>
28 #include <credentials/sets/mem_cred.h>
29
30 #include <openssl/cms.h>
31
32 #if OPENSSL_VERSION_NUMBER < 0x10100000L
33 #define X509_ATTRIBUTE_get0_object(attr) ({ (attr)->object; })
34 #endif
35
36 typedef struct private_openssl_pkcs7_t private_openssl_pkcs7_t;
37
38 /**
39 * Private data of an openssl_pkcs7_t object.
40 */
41 struct private_openssl_pkcs7_t {
42
43 /**
44 * Public pkcs7_t interface.
45 */
46 pkcs7_t public;
47
48 /**
49 * Type of this container
50 */
51 container_type_t type;
52
53 /**
54 * OpenSSL CMS structure
55 */
56 CMS_ContentInfo *cms;
57 };
58
59 /**
60 * OpenSSL does not allow us to read the signature to verify it with our own
61 * crypto API. We define the internal CMS_SignerInfo structure here to get it.
62 */
63 struct CMS_SignerInfo_st {
64 long version;
65 void *sid;
66 X509_ALGOR *digestAlgorithm;
67 STACK_OF(X509_ATTRIBUTE) *signedAttrs;
68 X509_ALGOR *signatureAlgorithm;
69 ASN1_OCTET_STRING *signature;
70 /* and more... */
71 };
72
73 /**
74 * And we also need access to the wrappend CMS_KeyTransRecipientInfo to
75 * read the encrypted key
76 */
77 struct CMS_KeyTransRecipientInfo_st {
78 long version;
79 void *rid;
80 X509_ALGOR *keyEncryptionAlgorithm;
81 ASN1_OCTET_STRING *encryptedKey;
82 };
83
84 struct CMS_RecipientInfo_st {
85 int type;
86 struct CMS_KeyTransRecipientInfo_st *ktri;
87 /* and more in union... */
88 };
89
90 struct CMS_EncryptedContentInfo_st {
91 ASN1_OBJECT *contentType;
92 X509_ALGOR *contentEncryptionAlgorithm;
93 ASN1_OCTET_STRING *encryptedContent;
94 /* and more... */
95 };
96
97 struct CMS_EnvelopedData_st {
98 long version;
99 void *originatorInfo;
100 STACK_OF(CMS_RecipientInfo) *recipientInfos;
101 struct CMS_EncryptedContentInfo_st *encryptedContentInfo;
102 /* and more... */
103 };
104
105 struct CMS_ContentInfo_st {
106 ASN1_OBJECT *contentType;
107 struct CMS_EnvelopedData_st *envelopedData;
108 /* and more in union... */
109 };
110
111 /**
112 * We can't include asn1.h, declare function prototypes directly
113 */
114 chunk_t asn1_wrap(int, const char *mode, ...);
115 int asn1_unwrap(chunk_t*, chunk_t*);
116
117 /**
118 * Enumerator over certificates
119 */
120 typedef struct {
121 /** implements enumerator_t */
122 enumerator_t public;
123 /** Stack of X509 certificates */
124 STACK_OF(X509) *certs;
125 /** current enumerator position in certificates */
126 int i;
127 /** currently enumerating certificate_t */
128 certificate_t *cert;
129 } cert_enumerator_t;
130
131 METHOD(enumerator_t, cert_destroy, void,
132 cert_enumerator_t *this)
133 {
134 DESTROY_IF(this->cert);
135 free(this);
136 }
137
138 METHOD(enumerator_t, cert_enumerate, bool,
139 cert_enumerator_t *this, certificate_t **out)
140 {
141 if (!this->certs)
142 {
143 return FALSE;
144 }
145 while (this->i < sk_X509_num(this->certs))
146 {
147 chunk_t encoding;
148 X509 *x509;
149
150 /* clean up previous round */
151 DESTROY_IF(this->cert);
152 this->cert = NULL;
153
154 x509 = sk_X509_value(this->certs, this->i++);
155 encoding = openssl_i2chunk(X509, x509);
156 this->cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
157 BUILD_BLOB_ASN1_DER, encoding,
158 BUILD_END);
159 free(encoding.ptr);
160 if (!this->cert)
161 {
162 continue;
163 }
164 *out = this->cert;
165 return TRUE;
166 }
167 return FALSE;
168 }
169
170 METHOD(pkcs7_t, create_cert_enumerator, enumerator_t*,
171 private_openssl_pkcs7_t *this)
172 {
173 cert_enumerator_t *enumerator;
174
175 if (this->type == CONTAINER_PKCS7_SIGNED_DATA)
176 {
177 INIT(enumerator,
178 .public = {
179 .enumerate = (void*)_cert_enumerate,
180 .destroy = _cert_destroy,
181 },
182 .certs = CMS_get1_certs(this->cms),
183 );
184 return &enumerator->public;
185 }
186 return enumerator_create_empty();
187 }
188
189 /**
190 * Enumerator for signatures
191 */
192 typedef struct {
193 /** implements enumerator_t */
194 enumerator_t public;
195 /** Stack of signerinfos */
196 STACK_OF(CMS_SignerInfo) *signers;
197 /** current enumerator position in signers */
198 int i;
199 /** currently enumerating auth config */
200 auth_cfg_t *auth;
201 /** full CMS */
202 CMS_ContentInfo *cms;
203 /** credential set containing wrapped certificates */
204 mem_cred_t *creds;
205 } signature_enumerator_t;
206
207 /**
208 * Verify signerInfo signature
209 */
210 static auth_cfg_t *verify_signature(CMS_SignerInfo *si, int hash_oid)
211 {
212 enumerator_t *enumerator;
213 public_key_t *key;
214 certificate_t *cert;
215 auth_cfg_t *auth, *found = NULL;
216 identification_t *issuer, *serial;
217 chunk_t attrs = chunk_empty, sig, attr;
218 X509_NAME *name;
219 ASN1_INTEGER *snr;
220 int i;
221
222 if (CMS_SignerInfo_get0_signer_id(si, NULL, &name, &snr) != 1)
223 {
224 return NULL;
225 }
226 issuer = openssl_x509_name2id(name);
227 if (!issuer)
228 {
229 return NULL;
230 }
231 serial = identification_create_from_encoding(
232 ID_KEY_ID, openssl_asn1_str2chunk(snr));
233
234 /* reconstruct DER encoded attributes to verify signature */
235 for (i = 0; i < CMS_signed_get_attr_count(si); i++)
236 {
237 attr = openssl_i2chunk(X509_ATTRIBUTE, CMS_signed_get_attr(si, i));
238 attrs = chunk_cat("mm", attrs, attr);
239 }
240 /* wrap in a ASN1_SET */
241 attrs = asn1_wrap(0x31, "m", attrs);
242
243 /* TODO: find a better way to access and verify the signature */
244 sig = openssl_asn1_str2chunk(si->signature);
245 enumerator = lib->credmgr->create_trusted_enumerator(lib->credmgr,
246 KEY_RSA, serial, FALSE);
247 while (enumerator->enumerate(enumerator, &cert, &auth))
248 {
249 if (issuer->equals(issuer, cert->get_issuer(cert)))
250 {
251 key = cert->get_public_key(cert);
252 if (key)
253 {
254 if (key->verify(key, signature_scheme_from_oid(hash_oid),
255 attrs, sig))
256 {
257 found = auth->clone(auth);
258 key->destroy(key);
259 break;
260 }
261 key->destroy(key);
262 }
263 }
264 }
265 enumerator->destroy(enumerator);
266 issuer->destroy(issuer);
267 serial->destroy(serial);
268 free(attrs.ptr);
269
270 return found;
271 }
272
273 /**
274 * Verify the message digest in the signerInfo attributes
275 */
276 static bool verify_digest(CMS_ContentInfo *cms, CMS_SignerInfo *si, int hash_oid)
277 {
278 ASN1_OCTET_STRING *os, **osp;
279 hash_algorithm_t hash_alg;
280 chunk_t digest, content, hash;
281 hasher_t *hasher;
282
283 os = CMS_signed_get0_data_by_OBJ(si,
284 OBJ_nid2obj(NID_pkcs9_messageDigest), -3, V_ASN1_OCTET_STRING);
285 if (!os)
286 {
287 return FALSE;
288 }
289 digest = openssl_asn1_str2chunk(os);
290 osp = CMS_get0_content(cms);
291 if (!osp)
292 {
293 return FALSE;
294 }
295 content = openssl_asn1_str2chunk(*osp);
296
297 hash_alg = hasher_algorithm_from_oid(hash_oid);
298 hasher = lib->crypto->create_hasher(lib->crypto, hash_alg);
299 if (!hasher)
300 {
301 DBG1(DBG_LIB, "hash algorithm %N not supported",
302 hash_algorithm_names, hash_alg);
303 return FALSE;
304 }
305 if (!hasher->allocate_hash(hasher, content, &hash))
306 {
307 hasher->destroy(hasher);
308 return FALSE;
309 }
310 hasher->destroy(hasher);
311
312 if (!chunk_equals_const(digest, hash))
313 {
314 free(hash.ptr);
315 DBG1(DBG_LIB, "invalid messageDigest");
316 return FALSE;
317 }
318 free(hash.ptr);
319 return TRUE;
320 }
321
322 METHOD(enumerator_t, signature_enumerate, bool,
323 signature_enumerator_t *this, auth_cfg_t **out)
324 {
325 if (!this->signers)
326 {
327 return FALSE;
328 }
329 while (this->i < sk_CMS_SignerInfo_num(this->signers))
330 {
331 CMS_SignerInfo *si;
332 X509_ALGOR *digest, *sig;
333 int hash_oid;
334
335 /* clean up previous round */
336 DESTROY_IF(this->auth);
337 this->auth = NULL;
338
339 si = sk_CMS_SignerInfo_value(this->signers, this->i++);
340
341 CMS_SignerInfo_get0_algs(si, NULL, NULL, &digest, &sig);
342 hash_oid = openssl_asn1_known_oid(digest->algorithm);
343 if (openssl_asn1_known_oid(sig->algorithm) != OID_RSA_ENCRYPTION)
344 {
345 DBG1(DBG_LIB, "only RSA digest encryption supported");
346 continue;
347 }
348 this->auth = verify_signature(si, hash_oid);
349 if (!this->auth)
350 {
351 DBG1(DBG_LIB, "unable to verify pkcs7 attributes signature");
352 continue;
353 }
354 if (!verify_digest(this->cms, si, hash_oid))
355 {
356 continue;
357 }
358 *out = this->auth;
359 return TRUE;
360 }
361 return FALSE;
362 }
363
364 METHOD(enumerator_t, signature_destroy, void,
365 signature_enumerator_t *this)
366 {
367 lib->credmgr->remove_local_set(lib->credmgr, &this->creds->set);
368 this->creds->destroy(this->creds);
369 DESTROY_IF(this->auth);
370 free(this);
371 }
372
373 METHOD(container_t, create_signature_enumerator, enumerator_t*,
374 private_openssl_pkcs7_t *this)
375 {
376 signature_enumerator_t *enumerator;
377
378 if (this->type == CONTAINER_PKCS7_SIGNED_DATA)
379 {
380 enumerator_t *certs;
381 certificate_t *cert;
382
383 INIT(enumerator,
384 .public = {
385 .enumerate = (void*)_signature_enumerate,
386 .destroy = _signature_destroy,
387 },
388 .cms = this->cms,
389 .signers = CMS_get0_SignerInfos(this->cms),
390 .creds = mem_cred_create(),
391 );
392
393 /* make available wrapped certs during signature checking */
394 certs = create_cert_enumerator(this);
395 while (certs->enumerate(certs, &cert))
396 {
397 enumerator->creds->add_cert(enumerator->creds, FALSE,
398 cert->get_ref(cert));
399 }
400 certs->destroy(certs);
401
402 lib->credmgr->add_local_set(lib->credmgr, &enumerator->creds->set,
403 FALSE);
404
405 return &enumerator->public;
406 }
407 return enumerator_create_empty();
408 }
409
410
411 METHOD(container_t, get_type, container_type_t,
412 private_openssl_pkcs7_t *this)
413 {
414 return this->type;
415 }
416
417 METHOD(pkcs7_t, get_attribute, bool,
418 private_openssl_pkcs7_t *this, int oid,
419 enumerator_t *enumerator, chunk_t *value)
420 {
421 signature_enumerator_t *e;
422 CMS_SignerInfo *si;
423 X509_ATTRIBUTE *attr;
424 ASN1_TYPE *type;
425 chunk_t chunk, wrapped;
426 int i;
427
428 e = (signature_enumerator_t*)enumerator;
429 if (e->i <= 0)
430 {
431 return FALSE;
432 }
433
434 /* "i" gets incremeneted after enumerate(), hence read from previous */
435 si = sk_CMS_SignerInfo_value(e->signers, e->i - 1);
436 for (i = 0; i < CMS_signed_get_attr_count(si); i++)
437 {
438 attr = CMS_signed_get_attr(si, i);
439 if (X509_ATTRIBUTE_count(attr) == 1 &&
440 openssl_asn1_known_oid(X509_ATTRIBUTE_get0_object(attr)) == oid)
441 {
442 /* get first value in SET */
443 type = X509_ATTRIBUTE_get0_type(attr, 0);
444 chunk = wrapped = openssl_i2chunk(ASN1_TYPE, type);
445 if (asn1_unwrap(&chunk, &chunk) != 0x100 /* ASN1_INVALID */)
446 {
447 *value = chunk_clone(chunk);
448 free(wrapped.ptr);
449 return TRUE;
450 }
451 free(wrapped.ptr);
452 }
453 }
454 return FALSE;
455 }
456
457 /**
458 * Find a private key for issuerAndSerialNumber
459 */
460 static private_key_t *find_private(identification_t *issuer,
461 identification_t *serial)
462 {
463 enumerator_t *enumerator;
464 certificate_t *cert;
465 public_key_t *public;
466 private_key_t *private = NULL;
467 identification_t *id;
468 chunk_t fp;
469
470 enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr,
471 CERT_X509, KEY_RSA, serial, FALSE);
472 while (enumerator->enumerate(enumerator, &cert))
473 {
474 if (issuer->equals(issuer, cert->get_issuer(cert)))
475 {
476 public = cert->get_public_key(cert);
477 if (public)
478 {
479 if (public->get_fingerprint(public, KEYID_PUBKEY_SHA1, &fp))
480 {
481 id = identification_create_from_encoding(ID_KEY_ID, fp);
482 private = lib->credmgr->get_private(lib->credmgr,
483 KEY_ANY, id, NULL);
484 id->destroy(id);
485 }
486 public->destroy(public);
487 }
488 }
489 if (private)
490 {
491 break;
492 }
493 }
494 enumerator->destroy(enumerator);
495 return private;
496 }
497
498 /**
499 * Decrypt enveloped-data with a decrypted symmetric key
500 */
501 static bool decrypt_symmetric(private_openssl_pkcs7_t *this, chunk_t key,
502 chunk_t encrypted, chunk_t *plain)
503 {
504 encryption_algorithm_t encr;
505 X509_ALGOR *alg;
506 crypter_t *crypter;
507 chunk_t iv;
508 size_t key_size;
509
510 /* read encryption algorithm from internal structures; TODO fixup */
511 alg = this->cms->envelopedData->encryptedContentInfo->
512 contentEncryptionAlgorithm;
513 encr = encryption_algorithm_from_oid(openssl_asn1_known_oid(alg->algorithm),
514 &key_size);
515 if (alg->parameter->type != V_ASN1_OCTET_STRING)
516 {
517 return FALSE;
518 }
519 iv = openssl_asn1_str2chunk(alg->parameter->value.octet_string);
520
521 crypter = lib->crypto->create_crypter(lib->crypto, encr, key_size / 8);
522 if (!crypter)
523 {
524 DBG1(DBG_LIB, "crypter %N-%d not available",
525 encryption_algorithm_names, alg, key_size);
526 return FALSE;
527 }
528 if (key.len != crypter->get_key_size(crypter))
529 {
530 DBG1(DBG_LIB, "symmetric key length is wrong");
531 crypter->destroy(crypter);
532 return FALSE;
533 }
534 if (iv.len != crypter->get_iv_size(crypter))
535 {
536 DBG1(DBG_LIB, "IV length is wrong");
537 crypter->destroy(crypter);
538 return FALSE;
539 }
540 if (!crypter->set_key(crypter, key) ||
541 !crypter->decrypt(crypter, encrypted, iv, plain))
542 {
543 crypter->destroy(crypter);
544 return FALSE;
545 }
546 crypter->destroy(crypter);
547 return TRUE;
548 }
549
550 /**
551 * Remove enveloped-data PKCS#7 padding from plain data
552 */
553 static bool remove_padding(chunk_t *data)
554 {
555 u_char *pos;
556 u_char pattern;
557 size_t padding;
558
559 if (!data->len)
560 {
561 return FALSE;
562 }
563 pos = data->ptr + data->len - 1;
564 padding = pattern = *pos;
565
566 if (padding > data->len)
567 {
568 DBG1(DBG_LIB, "padding greater than data length");
569 return FALSE;
570 }
571 data->len -= padding;
572
573 while (padding-- > 0)
574 {
575 if (*pos-- != pattern)
576 {
577 DBG1(DBG_LIB, "wrong padding pattern");
578 return FALSE;
579 }
580 }
581 return TRUE;
582 }
583
584 /**
585 * Decrypt PKCS#7 enveloped-data
586 */
587 static bool decrypt(private_openssl_pkcs7_t *this,
588 chunk_t encrypted, chunk_t *plain)
589 {
590 STACK_OF(CMS_RecipientInfo) *ris;
591 CMS_RecipientInfo *ri;
592 chunk_t chunk, key = chunk_empty;
593 int i;
594
595 ris = CMS_get0_RecipientInfos(this->cms);
596 for (i = 0; i < sk_CMS_RecipientInfo_num(ris); i++)
597 {
598 ri = sk_CMS_RecipientInfo_value(ris, i);
599 if (CMS_RecipientInfo_type(ri) == CMS_RECIPINFO_TRANS)
600 {
601 identification_t *serial, *issuer;
602 private_key_t *private;
603 X509_ALGOR *alg;
604 X509_NAME *name;
605 ASN1_INTEGER *sn;
606 u_char zero = 0;
607 int oid;
608
609 if (CMS_RecipientInfo_ktri_get0_algs(ri, NULL, NULL, &alg) == 1 &&
610 CMS_RecipientInfo_ktri_get0_signer_id(ri, NULL, &name, &sn) == 1)
611 {
612 oid = openssl_asn1_known_oid(alg->algorithm);
613 if (oid != OID_RSA_ENCRYPTION)
614 {
615 DBG1(DBG_LIB, "only RSA encryption supported in PKCS#7");
616 continue;
617 }
618 issuer = openssl_x509_name2id(name);
619 if (!issuer)
620 {
621 continue;
622 }
623 chunk = openssl_asn1_str2chunk(sn);
624 if (chunk.len && chunk.ptr[0] & 0x80)
625 { /* if MSB is set, append a zero to make it non-negative */
626 chunk = chunk_cata("cc", chunk_from_thing(zero), chunk);
627 }
628 serial = identification_create_from_encoding(ID_KEY_ID, chunk);
629 private = find_private(issuer, serial);
630 issuer->destroy(issuer);
631 serial->destroy(serial);
632
633 if (private)
634 {
635 /* get encryptedKey from internal structure; TODO fixup */
636 chunk = openssl_asn1_str2chunk(ri->ktri->encryptedKey);
637 if (private->decrypt(private, ENCRYPT_RSA_PKCS1,
638 chunk, &key))
639 {
640 private->destroy(private);
641 break;
642 }
643 private->destroy(private);
644 }
645 }
646 }
647 }
648 if (!key.len)
649 {
650 DBG1(DBG_LIB, "no private key found to decrypt PKCS#7");
651 return FALSE;
652 }
653 if (!decrypt_symmetric(this, key, encrypted, plain))
654 {
655 chunk_clear(&key);
656 return FALSE;
657 }
658 chunk_clear(&key);
659 if (!remove_padding(plain))
660 {
661 free(plain->ptr);
662 return FALSE;
663 }
664 return TRUE;
665 }
666
667 METHOD(container_t, get_data, bool,
668 private_openssl_pkcs7_t *this, chunk_t *data)
669 {
670 ASN1_OCTET_STRING **os;
671 chunk_t chunk;
672
673 os = CMS_get0_content(this->cms);
674 if (os)
675 {
676 chunk = openssl_asn1_str2chunk(*os);
677 switch (this->type)
678 {
679 case CONTAINER_PKCS7_DATA:
680 case CONTAINER_PKCS7_SIGNED_DATA:
681 *data = chunk_clone(chunk);
682 return TRUE;
683 case CONTAINER_PKCS7_ENVELOPED_DATA:
684 return decrypt(this, chunk, data);
685 default:
686 break;
687 }
688 }
689 return FALSE;
690 }
691
692 METHOD(container_t, get_encoding, bool,
693 private_openssl_pkcs7_t *this, chunk_t *data)
694 {
695 return FALSE;
696 }
697
698 METHOD(container_t, destroy, void,
699 private_openssl_pkcs7_t *this)
700 {
701 CMS_ContentInfo_free(this->cms);
702 free(this);
703 }
704
705 /**
706 * Generic constructor
707 */
708 static private_openssl_pkcs7_t* create_empty()
709 {
710 private_openssl_pkcs7_t *this;
711
712 INIT(this,
713 .public = {
714 .container = {
715 .get_type = _get_type,
716 .create_signature_enumerator = _create_signature_enumerator,
717 .get_data = _get_data,
718 .get_encoding = _get_encoding,
719 .destroy = _destroy,
720 },
721 .get_attribute = _get_attribute,
722 .create_cert_enumerator = _create_cert_enumerator,
723 },
724 );
725
726 return this;
727 }
728
729 /**
730 * Parse a PKCS#7 container
731 */
732 static bool parse(private_openssl_pkcs7_t *this, chunk_t blob)
733 {
734 BIO *bio;
735
736 bio = BIO_new_mem_buf(blob.ptr, blob.len);
737 this->cms = d2i_CMS_bio(bio, NULL);
738 BIO_free(bio);
739
740 if (!this->cms)
741 {
742 return FALSE;
743 }
744 switch (openssl_asn1_known_oid((ASN1_OBJECT*)CMS_get0_type(this->cms)))
745 {
746 case OID_PKCS7_DATA:
747 this->type = CONTAINER_PKCS7_DATA;
748 break;
749 case OID_PKCS7_SIGNED_DATA:
750 this->type = CONTAINER_PKCS7_SIGNED_DATA;
751 break;
752 case OID_PKCS7_ENVELOPED_DATA:
753 this->type = CONTAINER_PKCS7_ENVELOPED_DATA;
754 break;
755 default:
756 return FALSE;
757 }
758
759 return TRUE;
760 }
761
762 /**
763 * See header
764 */
765 pkcs7_t *openssl_pkcs7_load(container_type_t type, va_list args)
766 {
767 chunk_t blob = chunk_empty;
768 private_openssl_pkcs7_t *this;
769
770 while (TRUE)
771 {
772 switch (va_arg(args, builder_part_t))
773 {
774 case BUILD_BLOB_ASN1_DER:
775 blob = va_arg(args, chunk_t);
776 continue;
777 case BUILD_END:
778 break;
779 default:
780 return NULL;
781 }
782 break;
783 }
784 if (blob.len)
785 {
786 this = create_empty();
787 if (parse(this, blob))
788 {
789 return &this->public;
790 }
791 destroy(this);
792 }
793 return NULL;
794 }
795
796 #endif /* OPENSSL_NO_CMS */
797 #endif /* OPENSSL_VERSION_NUMBER */