534 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			534 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* fips/ecdsa/fips_ecdsavs.c */
 | 
						|
/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
 | 
						|
 * project.
 | 
						|
 */
 | 
						|
/* ====================================================================
 | 
						|
 * Copyright (c) 2011 The OpenSSL Project.  All rights reserved.
 | 
						|
 *
 | 
						|
 * Redistribution and use in source and binary forms, with or without
 | 
						|
 * modification, are permitted provided that the following conditions
 | 
						|
 * are met:
 | 
						|
 *
 | 
						|
 * 1. Redistributions of source code must retain the above copyright
 | 
						|
 *    notice, this list of conditions and the following disclaimer. 
 | 
						|
 *
 | 
						|
 * 2. Redistributions in binary form must reproduce the above copyright
 | 
						|
 *    notice, this list of conditions and the following disclaimer in
 | 
						|
 *    the documentation and/or other materials provided with the
 | 
						|
 *    distribution.
 | 
						|
 *
 | 
						|
 * 3. All advertising materials mentioning features or use of this
 | 
						|
 *    software must display the following acknowledgment:
 | 
						|
 *    "This product includes software developed by the OpenSSL Project
 | 
						|
 *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
 | 
						|
 *
 | 
						|
 * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
 | 
						|
 *    endorse or promote products derived from this software without
 | 
						|
 *    prior written permission. For written permission, please contact
 | 
						|
 *    licensing@OpenSSL.org.
 | 
						|
 *
 | 
						|
 * 5. Products derived from this software may not be called "OpenSSL"
 | 
						|
 *    nor may "OpenSSL" appear in their names without prior written
 | 
						|
 *    permission of the OpenSSL Project.
 | 
						|
 *
 | 
						|
 * 6. Redistributions of any form whatsoever must retain the following
 | 
						|
 *    acknowledgment:
 | 
						|
 *    "This product includes software developed by the OpenSSL Project
 | 
						|
 *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
 | 
						|
 *
 | 
						|
 * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
 | 
						|
 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
						|
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | 
						|
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
 | 
						|
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
						|
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 | 
						|
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 | 
						|
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 | 
						|
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 | 
						|
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 | 
						|
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 | 
						|
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
						|
 * ====================================================================
 | 
						|
 */
 | 
						|
 | 
						|
#define OPENSSL_FIPSAPI
 | 
						|
#include <openssl/opensslconf.h>
 | 
						|
#include <stdio.h>
 | 
						|
 | 
						|
#ifndef OPENSSL_FIPS
 | 
						|
 | 
						|
int main(int argc, char **argv)
 | 
						|
{
 | 
						|
    printf("No FIPS ECDSA support\n");
 | 
						|
    return(0);
 | 
						|
}
 | 
						|
#else
 | 
						|
 | 
						|
#include <string.h>
 | 
						|
#include <ctype.h>
 | 
						|
#include <openssl/err.h>
 | 
						|
#include <openssl/bn.h>
 | 
						|
#include <openssl/ecdsa.h>
 | 
						|
#include <openssl/evp.h>
 | 
						|
#include "fips_utl.h"
 | 
						|
 | 
						|
#include <openssl/objects.h>
 | 
						|
 | 
						|
 | 
						|
static int elookup_curve(char *in, char *curve_name, const EVP_MD **pmd)
 | 
						|
	{
 | 
						|
	char *cname, *p;
 | 
						|
	/* Copy buffer as we will change it */
 | 
						|
	strcpy(curve_name, in);
 | 
						|
	cname = curve_name + 1;
 | 
						|
	p = strchr(cname, ']');
 | 
						|
	if (!p)
 | 
						|
		{
 | 
						|
		fprintf(stderr, "Parse error: missing ]\n");
 | 
						|
		return NID_undef;
 | 
						|
		}
 | 
						|
	*p = 0;
 | 
						|
	p = strchr(cname, ',');
 | 
						|
	if (p)
 | 
						|
		{
 | 
						|
		if (!pmd)
 | 
						|
			{
 | 
						|
			fprintf(stderr, "Parse error: unexpected digest\n");
 | 
						|
			return NID_undef;
 | 
						|
			}
 | 
						|
		*p = 0;
 | 
						|
		p++;
 | 
						|
 | 
						|
		if (!strcmp(p, "SHA-1"))
 | 
						|
			*pmd = EVP_sha1();
 | 
						|
		else if (!strcmp(p, "SHA-224"))
 | 
						|
			*pmd = EVP_sha224();
 | 
						|
		else if (!strcmp(p, "SHA-256"))
 | 
						|
			*pmd = EVP_sha256();
 | 
						|
		else if (!strcmp(p, "SHA-384"))
 | 
						|
			*pmd = EVP_sha384();
 | 
						|
		else if (!strcmp(p, "SHA-512"))
 | 
						|
			*pmd = EVP_sha512();
 | 
						|
		else
 | 
						|
			{
 | 
						|
			fprintf(stderr, "Unknown digest %s\n", p);
 | 
						|
			return NID_undef;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	else if(pmd)
 | 
						|
		*pmd = EVP_sha1();
 | 
						|
 | 
						|
	if (!strcmp(cname, "B-163"))
 | 
						|
		return NID_sect163r2;
 | 
						|
	if (!strcmp(cname, "B-233"))
 | 
						|
		return NID_sect233r1;
 | 
						|
	if (!strcmp(cname, "B-283"))
 | 
						|
		return NID_sect283r1;
 | 
						|
	if (!strcmp(cname, "B-409"))
 | 
						|
		return NID_sect409r1;
 | 
						|
	if (!strcmp(cname, "B-571"))
 | 
						|
		return NID_sect571r1;
 | 
						|
	if (!strcmp(cname, "K-163"))
 | 
						|
		return NID_sect163k1;
 | 
						|
	if (!strcmp(cname, "K-233"))
 | 
						|
		return NID_sect233k1;
 | 
						|
	if (!strcmp(cname, "K-283"))
 | 
						|
		return NID_sect283k1;
 | 
						|
	if (!strcmp(cname, "K-409"))
 | 
						|
		return NID_sect409k1;
 | 
						|
	if (!strcmp(cname, "K-571"))
 | 
						|
		return NID_sect571k1;
 | 
						|
	if (!strcmp(cname, "P-192"))
 | 
						|
		return NID_X9_62_prime192v1;
 | 
						|
	if (!strcmp(cname, "P-224"))
 | 
						|
		return NID_secp224r1;
 | 
						|
	if (!strcmp(cname, "P-256"))
 | 
						|
		return NID_X9_62_prime256v1;
 | 
						|
	if (!strcmp(cname, "P-384"))
 | 
						|
		return NID_secp384r1;
 | 
						|
	if (!strcmp(cname, "P-521"))
 | 
						|
		return NID_secp521r1;
 | 
						|
 | 
						|
	fprintf(stderr, "Unknown Curve name %s\n", cname);
 | 
						|
	return NID_undef;
 | 
						|
	}
 | 
						|
 | 
						|
static int ec_get_pubkey(EC_KEY *key, BIGNUM *x, BIGNUM *y)
 | 
						|
	{
 | 
						|
	const EC_POINT *pt;
 | 
						|
	const EC_GROUP *grp;
 | 
						|
	const EC_METHOD *meth;
 | 
						|
	int rv;
 | 
						|
	BN_CTX *ctx;
 | 
						|
	ctx = BN_CTX_new();
 | 
						|
	if (!ctx)
 | 
						|
		return 0;
 | 
						|
	grp = EC_KEY_get0_group(key);
 | 
						|
	pt = EC_KEY_get0_public_key(key);
 | 
						|
	meth = EC_GROUP_method_of(grp);
 | 
						|
	if (EC_METHOD_get_field_type(meth) == NID_X9_62_prime_field)
 | 
						|
		rv = EC_POINT_get_affine_coordinates_GFp(grp, pt, x, y, ctx);
 | 
						|
	else
 | 
						|
#ifdef OPENSSL_NO_EC2M
 | 
						|
		{
 | 
						|
		fprintf(stderr, "ERROR: GF2m not supported\n");
 | 
						|
		exit(1);
 | 
						|
		}
 | 
						|
#else
 | 
						|
		rv = EC_POINT_get_affine_coordinates_GF2m(grp, pt, x, y, ctx);
 | 
						|
#endif
 | 
						|
 | 
						|
	BN_CTX_free(ctx);
 | 
						|
 | 
						|
	return rv;
 | 
						|
 | 
						|
	}
 | 
						|
 | 
						|
static int KeyPair(FILE *in, FILE *out)
 | 
						|
	{
 | 
						|
	char buf[2048], lbuf[2048];
 | 
						|
	char *keyword, *value;
 | 
						|
	int curve_nid = NID_undef;
 | 
						|
	int i, count;
 | 
						|
	BIGNUM *Qx = NULL, *Qy = NULL;
 | 
						|
	const BIGNUM *d = NULL;
 | 
						|
	EC_KEY *key = NULL;
 | 
						|
	Qx = BN_new();
 | 
						|
	Qy = BN_new();
 | 
						|
	while(fgets(buf, sizeof buf, in) != NULL)
 | 
						|
		{
 | 
						|
		if (*buf == '[' && buf[2] == '-')
 | 
						|
			{
 | 
						|
			if (buf[2] == '-')
 | 
						|
			curve_nid = elookup_curve(buf, lbuf, NULL);
 | 
						|
			fputs(buf, out);
 | 
						|
			continue;
 | 
						|
			}
 | 
						|
		if (!parse_line(&keyword, &value, lbuf, buf))
 | 
						|
			{
 | 
						|
			fputs(buf, out);
 | 
						|
			continue;
 | 
						|
			}
 | 
						|
		if (!strcmp(keyword, "N"))
 | 
						|
			{
 | 
						|
			count = atoi(value);
 | 
						|
 | 
						|
			for (i = 0; i < count; i++)
 | 
						|
				{
 | 
						|
 | 
						|
				key = EC_KEY_new_by_curve_name(curve_nid);
 | 
						|
				if (!EC_KEY_generate_key(key))
 | 
						|
					{
 | 
						|
					fprintf(stderr, "Error generating key\n");
 | 
						|
					return 0;
 | 
						|
					}
 | 
						|
 | 
						|
				if (!ec_get_pubkey(key, Qx, Qy))
 | 
						|
					{
 | 
						|
					fprintf(stderr, "Error getting public key\n");
 | 
						|
					return 0;
 | 
						|
					}
 | 
						|
 | 
						|
				d = EC_KEY_get0_private_key(key);
 | 
						|
 | 
						|
				do_bn_print_name(out, "d", d);
 | 
						|
				do_bn_print_name(out, "Qx", Qx);
 | 
						|
				do_bn_print_name(out, "Qy", Qy);
 | 
						|
				fputs(RESP_EOL, out);
 | 
						|
				EC_KEY_free(key);
 | 
						|
 | 
						|
				}
 | 
						|
 | 
						|
			}
 | 
						|
 | 
						|
		}
 | 
						|
	BN_free(Qx);
 | 
						|
	BN_free(Qy);
 | 
						|
	return 1;
 | 
						|
	}
 | 
						|
 | 
						|
static int PKV(FILE *in, FILE *out)
 | 
						|
	{
 | 
						|
 | 
						|
	char buf[2048], lbuf[2048];
 | 
						|
	char *keyword, *value;
 | 
						|
	int curve_nid = NID_undef;
 | 
						|
	BIGNUM *Qx = NULL, *Qy = NULL;
 | 
						|
	EC_KEY *key = NULL;
 | 
						|
	while(fgets(buf, sizeof buf, in) != NULL)
 | 
						|
		{
 | 
						|
		fputs(buf, out);
 | 
						|
		if (*buf == '[' && buf[2] == '-')
 | 
						|
			{
 | 
						|
			curve_nid = elookup_curve(buf, lbuf, NULL);
 | 
						|
			if (curve_nid == NID_undef)
 | 
						|
				return 0;
 | 
						|
				
 | 
						|
			}
 | 
						|
		if (!parse_line(&keyword, &value, lbuf, buf))
 | 
						|
			continue;
 | 
						|
		if (!strcmp(keyword, "Qx"))
 | 
						|
			{
 | 
						|
			if (!do_hex2bn(&Qx, value))
 | 
						|
				{
 | 
						|
				fprintf(stderr, "Invalid Qx value\n");
 | 
						|
				return 0;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		if (!strcmp(keyword, "Qy"))
 | 
						|
			{
 | 
						|
			int rv;
 | 
						|
			if (!do_hex2bn(&Qy, value))
 | 
						|
				{
 | 
						|
				fprintf(stderr, "Invalid Qy value\n");
 | 
						|
				return 0;
 | 
						|
				}
 | 
						|
			key = EC_KEY_new_by_curve_name(curve_nid);
 | 
						|
			no_err = 1;
 | 
						|
			rv = EC_KEY_set_public_key_affine_coordinates(key, Qx, Qy);
 | 
						|
			no_err = 0;
 | 
						|
			EC_KEY_free(key);
 | 
						|
			fprintf(out, "Result = %s" RESP_EOL, rv ? "P":"F");
 | 
						|
			}
 | 
						|
 | 
						|
		}
 | 
						|
	BN_free(Qx);
 | 
						|
	BN_free(Qy);
 | 
						|
	return 1;
 | 
						|
	}
 | 
						|
 | 
						|
static int SigGen(FILE *in, FILE *out)
 | 
						|
	{
 | 
						|
	char buf[2048], lbuf[2048];
 | 
						|
	char *keyword, *value;
 | 
						|
	unsigned char *msg;
 | 
						|
	int curve_nid = NID_undef;
 | 
						|
	long mlen;
 | 
						|
	BIGNUM *Qx = NULL, *Qy = NULL;
 | 
						|
	EC_KEY *key = NULL;
 | 
						|
	ECDSA_SIG *sig = NULL;
 | 
						|
	const EVP_MD *digest = NULL;
 | 
						|
	Qx = BN_new();
 | 
						|
	Qy = BN_new();
 | 
						|
	while(fgets(buf, sizeof buf, in) != NULL)
 | 
						|
		{
 | 
						|
		fputs(buf, out);
 | 
						|
		if (*buf == '[')
 | 
						|
			{
 | 
						|
			curve_nid = elookup_curve(buf, lbuf, &digest);
 | 
						|
			if (curve_nid == NID_undef)
 | 
						|
				return 0;
 | 
						|
			}
 | 
						|
		if (!parse_line(&keyword, &value, lbuf, buf))
 | 
						|
			continue;
 | 
						|
		if (!strcmp(keyword, "Msg"))
 | 
						|
			{
 | 
						|
			msg = hex2bin_m(value, &mlen);
 | 
						|
			if (!msg)
 | 
						|
				{
 | 
						|
				fprintf(stderr, "Invalid Message\n");
 | 
						|
				return 0;
 | 
						|
				}
 | 
						|
 | 
						|
			key = EC_KEY_new_by_curve_name(curve_nid);
 | 
						|
			if (!EC_KEY_generate_key(key))
 | 
						|
				{
 | 
						|
				fprintf(stderr, "Error generating key\n");
 | 
						|
				return 0;
 | 
						|
				}
 | 
						|
 | 
						|
			if (!ec_get_pubkey(key, Qx, Qy))
 | 
						|
				{
 | 
						|
				fprintf(stderr, "Error getting public key\n");
 | 
						|
				return 0;
 | 
						|
				}
 | 
						|
 | 
						|
	    		sig = FIPS_ecdsa_sign(key, msg, mlen, digest);
 | 
						|
 | 
						|
			if (!sig)
 | 
						|
				{
 | 
						|
				fprintf(stderr, "Error signing message\n");
 | 
						|
				return 0;
 | 
						|
				}
 | 
						|
 | 
						|
			do_bn_print_name(out, "Qx", Qx);
 | 
						|
			do_bn_print_name(out, "Qy", Qy);
 | 
						|
			do_bn_print_name(out, "R", sig->r);
 | 
						|
			do_bn_print_name(out, "S", sig->s);
 | 
						|
 | 
						|
			EC_KEY_free(key);
 | 
						|
			OPENSSL_free(msg);
 | 
						|
			FIPS_ecdsa_sig_free(sig);
 | 
						|
 | 
						|
			}
 | 
						|
 | 
						|
		}
 | 
						|
	BN_free(Qx);
 | 
						|
	BN_free(Qy);
 | 
						|
	return 1;
 | 
						|
	}
 | 
						|
 | 
						|
static int SigVer(FILE *in, FILE *out)
 | 
						|
	{
 | 
						|
	char buf[2048], lbuf[2048];
 | 
						|
	char *keyword, *value;
 | 
						|
	unsigned char *msg = NULL;
 | 
						|
	int curve_nid = NID_undef;
 | 
						|
	long mlen;
 | 
						|
	BIGNUM *Qx = NULL, *Qy = NULL;
 | 
						|
	EC_KEY *key = NULL;
 | 
						|
	ECDSA_SIG sg, *sig = &sg;
 | 
						|
	const EVP_MD *digest = NULL;
 | 
						|
	sig->r = NULL;
 | 
						|
	sig->s = NULL;
 | 
						|
	while(fgets(buf, sizeof buf, in) != NULL)
 | 
						|
		{
 | 
						|
		fputs(buf, out);
 | 
						|
		if (*buf == '[')
 | 
						|
			{
 | 
						|
			curve_nid = elookup_curve(buf, lbuf, &digest);
 | 
						|
			if (curve_nid == NID_undef)
 | 
						|
				return 0;
 | 
						|
			}
 | 
						|
		if (!parse_line(&keyword, &value, lbuf, buf))
 | 
						|
			continue;
 | 
						|
		if (!strcmp(keyword, "Msg"))
 | 
						|
			{
 | 
						|
			msg = hex2bin_m(value, &mlen);
 | 
						|
			if (!msg)
 | 
						|
				{
 | 
						|
				fprintf(stderr, "Invalid Message\n");
 | 
						|
				return 0;
 | 
						|
				}
 | 
						|
			}
 | 
						|
			
 | 
						|
		if (!strcmp(keyword, "Qx"))
 | 
						|
			{
 | 
						|
			if (!do_hex2bn(&Qx, value))
 | 
						|
				{
 | 
						|
				fprintf(stderr, "Invalid Qx value\n");
 | 
						|
				return 0;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		if (!strcmp(keyword, "Qy"))
 | 
						|
			{
 | 
						|
			if (!do_hex2bn(&Qy, value))
 | 
						|
				{
 | 
						|
				fprintf(stderr, "Invalid Qy value\n");
 | 
						|
				return 0;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		if (!strcmp(keyword, "R"))
 | 
						|
			{
 | 
						|
			if (!do_hex2bn(&sig->r, value))
 | 
						|
				{
 | 
						|
				fprintf(stderr, "Invalid R value\n");
 | 
						|
				return 0;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		if (!strcmp(keyword, "S"))
 | 
						|
			{
 | 
						|
			int rv;
 | 
						|
			if (!do_hex2bn(&sig->s, value))
 | 
						|
				{
 | 
						|
				fprintf(stderr, "Invalid S value\n");
 | 
						|
				return 0;
 | 
						|
				}
 | 
						|
			key = EC_KEY_new_by_curve_name(curve_nid);
 | 
						|
			rv = EC_KEY_set_public_key_affine_coordinates(key, Qx, Qy);
 | 
						|
 | 
						|
			if (rv != 1)
 | 
						|
				{
 | 
						|
				fprintf(stderr, "Error setting public key\n");
 | 
						|
				return 0;
 | 
						|
				}
 | 
						|
 | 
						|
			no_err = 1;
 | 
						|
	    		rv = FIPS_ecdsa_verify(key, msg, mlen, digest, sig);
 | 
						|
			EC_KEY_free(key);
 | 
						|
			if (msg)
 | 
						|
				OPENSSL_free(msg);
 | 
						|
			no_err = 0;
 | 
						|
 | 
						|
			fprintf(out, "Result = %s" RESP_EOL, rv ? "P":"F");
 | 
						|
			}
 | 
						|
 | 
						|
		}
 | 
						|
	if (sig->r)
 | 
						|
		BN_free(sig->r);
 | 
						|
	if (sig->s)
 | 
						|
		BN_free(sig->s);
 | 
						|
	if (Qx)
 | 
						|
		BN_free(Qx);
 | 
						|
	if (Qy)
 | 
						|
		BN_free(Qy);
 | 
						|
	return 1;
 | 
						|
	}
 | 
						|
#ifdef FIPS_ALGVS
 | 
						|
int fips_ecdsavs_main(int argc, char **argv)
 | 
						|
#else
 | 
						|
int main(int argc, char **argv)
 | 
						|
#endif
 | 
						|
	{
 | 
						|
	FILE *in = NULL, *out = NULL;
 | 
						|
	const char *cmd = argv[1];
 | 
						|
	int rv = 0;
 | 
						|
	fips_algtest_init();
 | 
						|
 | 
						|
	if (argc == 4)
 | 
						|
		{
 | 
						|
		in = fopen(argv[2], "r");
 | 
						|
		if (!in)
 | 
						|
			{
 | 
						|
			fprintf(stderr, "Error opening input file\n");
 | 
						|
			exit(1);
 | 
						|
			}
 | 
						|
		out = fopen(argv[3], "w");
 | 
						|
		if (!out)
 | 
						|
			{
 | 
						|
			fprintf(stderr, "Error opening output file\n");
 | 
						|
			exit(1);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	else if (argc == 2)
 | 
						|
		{
 | 
						|
		in = stdin;
 | 
						|
		out = stdout;
 | 
						|
		}
 | 
						|
 | 
						|
	if (!cmd)
 | 
						|
		{
 | 
						|
		fprintf(stderr, "fips_ecdsavs [KeyPair|PKV|SigGen|SigVer]\n");
 | 
						|
		return 1;
 | 
						|
		}
 | 
						|
	if (!strcmp(cmd, "KeyPair"))
 | 
						|
		rv = KeyPair(in, out);
 | 
						|
	else if (!strcmp(cmd, "PKV"))
 | 
						|
		rv = PKV(in, out);
 | 
						|
	else if (!strcmp(cmd, "SigVer"))
 | 
						|
		rv = SigVer(in, out);
 | 
						|
	else if (!strcmp(cmd, "SigGen"))
 | 
						|
		rv = SigGen(in, out);
 | 
						|
	else
 | 
						|
		{
 | 
						|
		fprintf(stderr, "Unknown command %s\n", cmd);
 | 
						|
		return 1;
 | 
						|
		}
 | 
						|
 | 
						|
	if (argc == 4)
 | 
						|
		{
 | 
						|
		fclose(in);
 | 
						|
		fclose(out);
 | 
						|
		}
 | 
						|
 | 
						|
	if (rv <= 0)
 | 
						|
		{
 | 
						|
		fprintf(stderr, "Error running %s\n", cmd);
 | 
						|
		return 1;
 | 
						|
		}
 | 
						|
 | 
						|
	return 0;
 | 
						|
	}
 | 
						|
 | 
						|
#endif
 |