diff --git a/openpgpsdk/include/openpgpsdk/accumulate.h b/openpgpsdk/include/openpgpsdk/accumulate.h new file mode 100644 index 000000000..eeadb4011 --- /dev/null +++ b/openpgpsdk/include/openpgpsdk/accumulate.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + */ + +#ifndef OPS_ACCUMULATE_H +#define OPS_ACCUMULATE_H +#endif + +#include "keyring.h" +#include "packet-parse.h" + +int ops_parse_and_accumulate(ops_keyring_t *keyring, + ops_parse_info_t *parse_info); diff --git a/openpgpsdk/include/openpgpsdk/armour.h b/openpgpsdk/include/openpgpsdk/armour.h new file mode 100644 index 000000000..b2f3a8d69 --- /dev/null +++ b/openpgpsdk/include/openpgpsdk/armour.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OPS_ARMOUR_H__ +#define __OPS_ARMOUR_H__ + +#include "packet-parse.h" +#include "signature.h" + +unsigned ops_crc24(unsigned checksum,unsigned char c); + +void ops_reader_push_dearmour(ops_parse_info_t *parse_info); + +void ops_reader_pop_dearmour(ops_parse_info_t *parse_info); +ops_boolean_t ops_writer_push_clearsigned(ops_create_info_t *info, + ops_create_signature_t *sig); +void ops_writer_push_armoured_message(ops_create_info_t *info); +ops_boolean_t ops_writer_switch_to_armoured_signature(ops_create_info_t *info); + +typedef enum + { + OPS_PGP_MESSAGE=1, + OPS_PGP_PUBLIC_KEY_BLOCK, + OPS_PGP_PRIVATE_KEY_BLOCK, + OPS_PGP_MULTIPART_MESSAGE_PART_X_OF_Y, + OPS_PGP_MULTIPART_MESSAGE_PART_X, + OPS_PGP_SIGNATURE + } ops_armor_type_t; + +void ops_writer_push_armoured(ops_create_info_t *info, ops_armor_type_t type); + +#define CRC24_INIT 0xb704ceL + +#endif /* __OPS_ARMOUR_H__ */ + +// EOF diff --git a/openpgpsdk/include/openpgpsdk/callback.h b/openpgpsdk/include/openpgpsdk/callback.h new file mode 100644 index 000000000..399c3d118 --- /dev/null +++ b/openpgpsdk/include/openpgpsdk/callback.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OPS_CALLBACK_H__ +#define __OPS_CALLBACK_H__ + +#define CB(cbinfo,t,pc) do { (pc)->tag=(t); if(ops_parse_cb((pc),(cbinfo)) == OPS_RELEASE_MEMORY) ops_parser_content_free(pc); } while(0) +/*#define CB(cbinfo,t,pc) do { (pc)->tag=(t); if((cbinfo)->cb(pc,(cbinfo)) == OPS_RELEASE_MEMORY) ops_parser_content_free(pc); } while(0)*/ +//#define CB(cbinfo,t,pc) do { (pc)->tag=(t); if((cbinfo)->cb(pc,(cbinfo)) == OPS_RELEASE_MEMORY) ops_parser_content_free(pc); } while(0) + +#define CBP(info,t,pc) CB(&(info)->cbinfo,t,pc) + +#define ERR(cbinfo,err,code) do { content.content.error.error=err; content.tag=OPS_PARSER_ERROR; ops_parse_cb(&content,(cbinfo)); OPS_ERROR(errors,code,err); return -1; } while(0) + /*#define ERR(err) do { content.content.error.error=err; content.tag=OPS_PARSER_ERROR; ops_parse_cb(&content,cbinfo); return -1; } while(0)*/ + +#define ERRP(info,err) do { C.error.error=err; CBP(info,OPS_PARSER_ERROR,&content); return ops_false; } while(0) + + +#endif /*__OPS_CALLBACK_H__*/ diff --git a/openpgpsdk/include/openpgpsdk/compress.h b/openpgpsdk/include/openpgpsdk/compress.h new file mode 100644 index 000000000..49fb286d7 --- /dev/null +++ b/openpgpsdk/include/openpgpsdk/compress.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + */ + +#include "packet-parse.h" + +int ops_decompress(ops_region_t *region,ops_parse_info_t *parse_info, + ops_compression_type_t type); + +ops_boolean_t ops_write_compressed(const unsigned char* data, + const unsigned int len, + ops_create_info_t *cinfo); diff --git a/openpgpsdk/include/openpgpsdk/configure.h b/openpgpsdk/include/openpgpsdk/configure.h new file mode 100644 index 000000000..38240da1a --- /dev/null +++ b/openpgpsdk/include/openpgpsdk/configure.h @@ -0,0 +1,7 @@ +/* generated by configure from include/openpgpsdk/configure.h.template. Don't edit. */ + +#define HAVE_ALLOCA_H 0 +#define TIME_T_FMT "%ld" + +/* for silencing unused parameter warnings */ +#define OPS_USED(x) (x)=(x) diff --git a/openpgpsdk/include/openpgpsdk/create.h b/openpgpsdk/include/openpgpsdk/create.h new file mode 100644 index 000000000..3ef24854e --- /dev/null +++ b/openpgpsdk/include/openpgpsdk/create.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + */ + +#ifndef OPS_CREATE_H +#define OPS_CREATE_H + +#include +#include +#include +#include +#include +#include +#include + +/** + * \ingroup Create + * This struct contains the required information about how to write this stream + */ +struct ops_create_info + { + ops_writer_info_t winfo; + ops_error_t *errors; /*!< an error stack */ + }; + +ops_create_info_t *ops_create_info_new(void); +void ops_create_info_delete(ops_create_info_t *info); + +ops_memory_t* ops_write_mem_from_file(const char *filename, int* errnum); +int ops_write_file_from_buf(const char *filename, const char* buf, const size_t len, const ops_boolean_t overwrite); + +ops_boolean_t ops_calc_session_key_checksum(ops_pk_session_key_t *session_key, unsigned char *cs); +void ops_build_public_key(ops_memory_t *out,const ops_public_key_t *key, + ops_boolean_t make_packet); +ops_boolean_t ops_write_struct_user_id(ops_user_id_t *id, + ops_create_info_t *info); +ops_boolean_t ops_write_struct_public_key(const ops_public_key_t *key, + ops_create_info_t *info); + +ops_boolean_t ops_write_ss_header(unsigned length,ops_content_tag_t type, + ops_create_info_t *info); +ops_boolean_t ops_write_struct_secret_key(const ops_secret_key_t *key, + const unsigned char* passphrase, + const size_t pplen, + ops_create_info_t *info); +ops_boolean_t ops_write_one_pass_sig(const ops_secret_key_t* skey, + const ops_hash_algorithm_t hash_alg, + const ops_sig_type_t sig_type, + ops_create_info_t* info); +ops_boolean_t ops_write_literal_data_from_buf(const unsigned char *data, + const int maxlen, + const ops_literal_data_type_t type, + ops_create_info_t *info); +ops_pk_session_key_t *ops_create_pk_session_key(const ops_keydata_t *key); +ops_boolean_t ops_write_pk_session_key(ops_create_info_t *info, + ops_pk_session_key_t *pksk); +ops_boolean_t ops_write_transferable_public_key(const ops_keydata_t *key, ops_boolean_t armoured, ops_create_info_t *info); +ops_boolean_t ops_write_transferable_secret_key(const ops_keydata_t *key, const unsigned char* passphrase, const size_t pplen, ops_boolean_t armoured, ops_create_info_t *info); + +#endif /*OPS_CREATE_H*/ + +// eof diff --git a/openpgpsdk/include/openpgpsdk/crypto.h b/openpgpsdk/include/openpgpsdk/crypto.h new file mode 100644 index 000000000..a7f09529d --- /dev/null +++ b/openpgpsdk/include/openpgpsdk/crypto.h @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + */ + +#ifndef OPS_CRYPTO_H +#define OPS_CRYPTO_H + +#include "keyring.h" +#include "util.h" +#include "packet.h" +#include "packet-parse.h" +#include + +#define OPS_MIN_HASH_SIZE 16 + +typedef void ops_hash_init_t(ops_hash_t *hash); +typedef void ops_hash_add_t(ops_hash_t *hash,const unsigned char *data, + unsigned length); +typedef unsigned ops_hash_finish_t(ops_hash_t *hash,unsigned char *out); + +/** _ops_hash_t */ +struct _ops_hash_t + { + ops_hash_algorithm_t algorithm; + size_t size; + const char *name; + ops_hash_init_t *init; + ops_hash_add_t *add; + ops_hash_finish_t *finish; + void *data; + }; + +typedef void ops_crypt_set_iv_t(ops_crypt_t *crypt, + const unsigned char *iv); +typedef void ops_crypt_set_key_t(ops_crypt_t *crypt, + const unsigned char *key); +typedef void ops_crypt_init_t(ops_crypt_t *crypt); +typedef void ops_crypt_resync_t(ops_crypt_t *crypt); +typedef void ops_crypt_block_encrypt_t(ops_crypt_t *crypt,void *out, + const void *in); +typedef void ops_crypt_block_decrypt_t(ops_crypt_t *crypt,void *out, + const void *in); +typedef void ops_crypt_cfb_encrypt_t(ops_crypt_t *crypt,void *out, + const void *in, size_t count); +typedef void ops_crypt_cfb_decrypt_t(ops_crypt_t *crypt,void *out, + const void *in, size_t count); +typedef void ops_crypt_finish_t(ops_crypt_t *crypt); + +/** _ops_crypt_t */ +struct _ops_crypt_t + { + ops_symmetric_algorithm_t algorithm; + size_t blocksize; + size_t keysize; + ops_crypt_set_iv_t *set_iv; /* Call this before decrypt init! */ + ops_crypt_set_key_t *set_key; /* Call this before init! */ + ops_crypt_init_t *base_init; + ops_crypt_resync_t *decrypt_resync; + // encrypt/decrypt one block + ops_crypt_block_encrypt_t *block_encrypt; + ops_crypt_block_decrypt_t *block_decrypt; + + // Standard CFB encrypt/decrypt (as used by Sym Enc Int Prot packets) + ops_crypt_cfb_encrypt_t *cfb_encrypt; + ops_crypt_cfb_decrypt_t *cfb_decrypt; + + ops_crypt_finish_t *decrypt_finish; + unsigned char iv[OPS_MAX_BLOCK_SIZE]; + unsigned char civ[OPS_MAX_BLOCK_SIZE]; + unsigned char siv[OPS_MAX_BLOCK_SIZE]; /* Needed for weird v3 resync */ + unsigned char key[OPS_MAX_KEY_SIZE]; + size_t num; /* Offset - see openssl _encrypt doco */ + void *encrypt_key; + void *decrypt_key; + }; + +void ops_crypto_init(void); +void ops_crypto_finish(void); +void ops_hash_md5(ops_hash_t *hash); +void ops_hash_sha1(ops_hash_t *hash); +void ops_hash_sha256(ops_hash_t *hash); +void ops_hash_sha512(ops_hash_t *hash); +void ops_hash_sha384(ops_hash_t *hash); +void ops_hash_sha224(ops_hash_t *hash); +void ops_hash_any(ops_hash_t *hash,ops_hash_algorithm_t alg); +ops_hash_algorithm_t ops_hash_algorithm_from_text(const char *hash); +const char *ops_text_from_hash(ops_hash_t *hash); +unsigned ops_hash_size(ops_hash_algorithm_t alg); +unsigned ops_hash(unsigned char *out,ops_hash_algorithm_t alg,const void *in, + size_t length); + +void ops_hash_add_int(ops_hash_t *hash,unsigned n,unsigned length); + +ops_boolean_t ops_dsa_verify(const unsigned char *hash,size_t hash_length, + const ops_dsa_signature_t *sig, + const ops_dsa_public_key_t *dsa); +int ops_rsa_public_decrypt(unsigned char *out,const unsigned char *in, + size_t length,const ops_rsa_public_key_t *rsa); +int ops_rsa_public_encrypt(unsigned char *out,const unsigned char *in, + size_t length,const ops_rsa_public_key_t *rsa); +int ops_rsa_private_encrypt(unsigned char *out,const unsigned char *in, + size_t length,const ops_rsa_secret_key_t *srsa, + const ops_rsa_public_key_t *rsa); +int ops_rsa_private_decrypt(unsigned char *out,const unsigned char *in, + size_t length,const ops_rsa_secret_key_t *srsa, + const ops_rsa_public_key_t *rsa); + +unsigned ops_block_size(ops_symmetric_algorithm_t alg); +unsigned ops_key_size(ops_symmetric_algorithm_t alg); + +int ops_decrypt_data(ops_content_tag_t tag,ops_region_t *region, + ops_parse_info_t *parse_info); + +int ops_crypt_any(ops_crypt_t *decrypt,ops_symmetric_algorithm_t alg); +void ops_decrypt_init(ops_crypt_t *decrypt); +void ops_encrypt_init(ops_crypt_t *encrypt); +size_t ops_decrypt_se(ops_crypt_t *decrypt,void *out,const void *in, + size_t count); +size_t ops_encrypt_se(ops_crypt_t *encrypt,void *out,const void *in, + size_t count); +size_t ops_decrypt_se_ip(ops_crypt_t *decrypt,void *out,const void *in, + size_t count); +size_t ops_encrypt_se_ip(ops_crypt_t *encrypt,void *out,const void *in, + size_t count); +ops_boolean_t ops_is_sa_supported(ops_symmetric_algorithm_t alg); + +void ops_reader_push_decrypt(ops_parse_info_t *pinfo,ops_crypt_t *decrypt, + ops_region_t *region); +void ops_reader_pop_decrypt(ops_parse_info_t *pinfo); + +// Hash everything that's read +void ops_reader_push_hash(ops_parse_info_t *pinfo,ops_hash_t *hash); +void ops_reader_pop_hash(ops_parse_info_t *pinfo); + +int ops_decrypt_and_unencode_mpi(unsigned char *buf,unsigned buflen,const BIGNUM *encmpi, + const ops_secret_key_t *skey); +ops_boolean_t ops_rsa_encrypt_mpi(const unsigned char *buf, const size_t buflen, + const ops_public_key_t *pkey, + ops_pk_session_key_parameters_t *spk); + + +// Encrypt everything that's written +struct ops_key_data; +void ops_writer_push_encrypt(ops_create_info_t *info, + const struct ops_key_data *key); + +ops_boolean_t ops_encrypt_file(const char* input_filename, const char* output_filename, const ops_keydata_t *pub_key, const ops_boolean_t use_armour, const ops_boolean_t allow_overwrite); +ops_boolean_t ops_decrypt_file(const char* input_filename, const char* output_filename, ops_keyring_t *keyring, const ops_boolean_t use_armour, const ops_boolean_t allow_overwrite,ops_parse_cb_t* cb_get_passphrase); + +// Keys +ops_boolean_t ops_rsa_generate_keypair(const int numbits, const unsigned long e, ops_keydata_t* keydata); +ops_keydata_t* ops_rsa_create_selfsigned_keypair(const int numbits, const unsigned long e, ops_user_id_t * userid); + +int ops_dsa_size(const ops_dsa_public_key_t *dsa); +DSA_SIG* ops_dsa_sign(unsigned char* hashbuf, unsigned hashsize, const ops_dsa_secret_key_t *sdsa, const ops_dsa_public_key_t *dsa); +#endif diff --git a/openpgpsdk/include/openpgpsdk/defs.h b/openpgpsdk/include/openpgpsdk/defs.h new file mode 100644 index 000000000..e4f8b48ba --- /dev/null +++ b/openpgpsdk/include/openpgpsdk/defs.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + */ + +#ifndef OPS_DEFS_H +#define OPS_DEFS_H + +#define OPS_ARMOURED ops_true +#define OPS_UNARMOURED ops_false + +#define OPS_OVERWRITE_YES ops_true +#define OPS_OVERWRITE_NO ops_false + +#define OPS_ACCUMULATE_YES ops_true +#define OPS_ACCUMULATE_NO ops_false + +#endif /* OPS_DEFS_H */ diff --git a/openpgpsdk/include/openpgpsdk/errors.h b/openpgpsdk/include/openpgpsdk/errors.h new file mode 100644 index 000000000..e67e71d1e --- /dev/null +++ b/openpgpsdk/include/openpgpsdk/errors.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + */ + +#ifndef OPS_ERRORS +#define OPS_ERRORS + +#include "openpgpsdk/types.h" +#include + +/** error codes */ +// Remember to add names to map in errors.c +typedef enum + { + OPS_E_OK=0x0000, /* no error */ + OPS_E_FAIL=0x0001, /* general error */ + OPS_E_SYSTEM_ERROR=0x0002, /* system error, look at errno for details */ + OPS_E_UNIMPLEMENTED=0x0003, /* feature not yet implemented */ + + /* reader errors */ + OPS_E_R=0x1000, /* general reader error */ + OPS_E_R_READ_FAILED =OPS_E_R+1, + OPS_E_R_EARLY_EOF =OPS_E_R+2, + OPS_E_R_BAD_FORMAT =OPS_E_R+3, // For example, malformed armour + OPS_E_R_UNSUPPORTED =OPS_E_R+4, + OPS_E_R_UNCONSUMED_DATA =OPS_E_R+5, + + /* writer errors */ + OPS_E_W=0x2000, /* general writer error */ + OPS_E_W_WRITE_FAILED = OPS_E_W+1, + OPS_E_W_WRITE_TOO_SHORT = OPS_E_W+2, + + /* parser errors */ + OPS_E_P=0x3000, /* general parser error */ + OPS_E_P_NOT_ENOUGH_DATA =OPS_E_P+1, + OPS_E_P_UNKNOWN_TAG =OPS_E_P+2, + OPS_E_P_PACKET_CONSUMED =OPS_E_P+3, + OPS_E_P_MPI_FORMAT_ERROR =OPS_E_P+4, + OPS_E_P_PACKET_NOT_CONSUMED =OPS_E_P+5, + OPS_E_P_DECOMPRESSION_ERROR =OPS_E_P+6, + OPS_E_P_NO_USERID =OPS_E_P+7, + + /* creator errors */ + OPS_E_C=0x4000, /* general creator error */ + + /* validation errors */ + OPS_E_V=0x5000, /* general validation error */ + OPS_E_V_BAD_SIGNATURE =OPS_E_V+1, + OPS_E_V_NO_SIGNATURE =OPS_E_V+2, + OPS_E_V_UNKNOWN_SIGNER =OPS_E_V+3, + OPS_E_V_BAD_HASH =OPS_E_V+4, + + /* Algorithm support errors */ + OPS_E_ALG=0x6000, /* general algorithm error */ + OPS_E_ALG_UNSUPPORTED_SYMMETRIC_ALG =OPS_E_ALG+1, + OPS_E_ALG_UNSUPPORTED_PUBLIC_KEY_ALG =OPS_E_ALG+2, + OPS_E_ALG_UNSUPPORTED_SIGNATURE_ALG =OPS_E_ALG+3, + OPS_E_ALG_UNSUPPORTED_HASH_ALG =OPS_E_ALG+4, + OPS_E_ALG_UNSUPPORTED_COMPRESS_ALG =OPS_E_ALG+5, + + /* Protocol errors */ + OPS_E_PROTO=0x7000, /* general protocol error */ + OPS_E_PROTO_BAD_SYMMETRIC_DECRYPT =OPS_E_PROTO+2, + OPS_E_PROTO_UNKNOWN_SS =OPS_E_PROTO+3, + OPS_E_PROTO_CRITICAL_SS_IGNORED =OPS_E_PROTO+4, + OPS_E_PROTO_BAD_PUBLIC_KEY_VRSN =OPS_E_PROTO+5, + OPS_E_PROTO_BAD_SIGNATURE_VRSN =OPS_E_PROTO+6, + OPS_E_PROTO_BAD_ONE_PASS_SIG_VRSN =OPS_E_PROTO+7, + OPS_E_PROTO_BAD_PKSK_VRSN =OPS_E_PROTO+8, + OPS_E_PROTO_DECRYPTED_MSG_WRONG_LEN =OPS_E_PROTO+9, + OPS_E_PROTO_BAD_SK_CHECKSUM =OPS_E_PROTO+10, + } ops_errcode_t; + +/** ops_errcode_name_map_t */ +typedef ops_map_t ops_errcode_name_map_t; + +/** one entry in a linked list of errors */ +typedef struct ops_error + { + ops_errcode_t errcode; + int sys_errno; /*!< irrelevent unless errcode == OPS_E_SYSTEM_ERROR */ + char *comment; + const char *file; + int line; + struct ops_error *next; + } ops_error_t; + +char *ops_errcode(const ops_errcode_t errcode); + +void ops_push_error(ops_error_t **errstack,ops_errcode_t errcode,int sys_errno, + const char *file,int line,const char *comment,...); +void ops_print_error(ops_error_t *err); +void ops_print_errors(ops_error_t *errstack); +void ops_free_errors(ops_error_t *errstack); +int ops_has_error(ops_error_t *errstack, ops_errcode_t errcode); + +#define OPS_SYSTEM_ERROR_1(err,code,syscall,fmt,arg) do { ops_push_error(err,OPS_E_SYSTEM_ERROR,errno,__FILE__,__LINE__,syscall); ops_push_error(err,code,0,__FILE__,__LINE__,fmt,arg); } while(0) +#define OPS_MEMORY_ERROR(err) {fprintf(stderr, "Memory error\n");} // \todo placeholder for better error handling +#define OPS_ERROR(err,code,fmt) do { ops_push_error(err,code,0,__FILE__,__LINE__,fmt); } while(0) +#define OPS_ERROR_1(err,code,fmt,arg) do { ops_push_error(err,code,0,__FILE__,__LINE__,fmt,arg); } while(0) +#define OPS_ERROR_2(err,code,fmt,arg,arg2) do { ops_push_error(err,code,0,__FILE__,__LINE__,fmt,arg,arg2); } while(0) +#define OPS_ERROR_3(err,code,fmt,arg,arg2,arg3) do { ops_push_error(err,code,0,__FILE__,__LINE__,fmt,arg,arg2,arg3); } while(0) +#define OPS_ERROR_4(err,code,fmt,arg,arg2,arg3,arg4) do { ops_push_error(err,code,0,__FILE__,__LINE__,fmt,arg,arg2,arg3,arg4); } while(0) + +#endif /* OPS_ERRORS */ diff --git a/openpgpsdk/include/openpgpsdk/final.h b/openpgpsdk/include/openpgpsdk/final.h new file mode 100644 index 000000000..404391d2f --- /dev/null +++ b/openpgpsdk/include/openpgpsdk/final.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* A header that is always included last, for things that need to come last */ + +#ifdef DMALLOC +# include +#endif + diff --git a/openpgpsdk/include/openpgpsdk/hash.h b/openpgpsdk/include/openpgpsdk/hash.h new file mode 100644 index 000000000..10b4f32d0 --- /dev/null +++ b/openpgpsdk/include/openpgpsdk/hash.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OPS_HASH_H__ +#define __OPS_HASH_H__ +#include "openpgpsdk/packet.h" + +void ops_calc_mdc_hash(const unsigned char* preamble, const size_t sz_preamble, const unsigned char* plaintext, const unsigned int sz_plaintext, unsigned char *hashed); +ops_boolean_t ops_is_hash_alg_supported(const ops_hash_algorithm_t *hash_alg); + +#endif /*__OPS_HASH_H__*/ diff --git a/openpgpsdk/include/openpgpsdk/keyring.h b/openpgpsdk/include/openpgpsdk/keyring.h new file mode 100644 index 000000000..db08b9fab --- /dev/null +++ b/openpgpsdk/include/openpgpsdk/keyring.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + */ + +#ifndef OPS_KEYRING_H +#define OPS_KEYRING_H + +#include "packet.h" +#include "memory.h" + +typedef struct ops_keydata ops_keydata_t; + +/** \struct ops_keyring_t + * A keyring + */ + +typedef struct + { + int nkeys; // while we are constructing a key, this is the offset + int nkeys_allocated; + ops_keydata_t *keys; + } ops_keyring_t; + +const ops_keydata_t * +ops_keyring_find_key_by_id(const ops_keyring_t *keyring, + const unsigned char keyid[OPS_KEY_ID_SIZE]); +const ops_keydata_t * +ops_keyring_find_key_by_userid(const ops_keyring_t *keyring, + const char* userid); +void ops_keydata_free(ops_keydata_t *key); +void ops_keyring_free(ops_keyring_t *keyring); +void ops_dump_keyring(const ops_keyring_t *keyring); +const ops_public_key_t * +ops_get_public_key_from_data(const ops_keydata_t *data); +ops_boolean_t ops_is_key_secret(const ops_keydata_t *data); +const ops_secret_key_t * +ops_get_secret_key_from_data(const ops_keydata_t *data); +ops_secret_key_t * +ops_get_writable_secret_key_from_data(ops_keydata_t *data); +ops_secret_key_t *ops_decrypt_secret_key_from_data(const ops_keydata_t *key, + const char *pphrase); + +ops_boolean_t ops_keyring_read_from_file(ops_keyring_t *keyring, const ops_boolean_t armour, const char *filename); +ops_boolean_t ops_keyring_read_from_mem(ops_keyring_t *keyring, const ops_boolean_t armour, ops_memory_t *mem); + +char *ops_malloc_passphrase(char *passphrase); +char *ops_get_passphrase(void); + +void ops_keyring_list(const ops_keyring_t* keyring); + +void ops_set_secret_key(ops_parser_content_union_t* content,const ops_keydata_t *key); + +const unsigned char* ops_get_key_id(const ops_keydata_t *key); +unsigned ops_get_user_id_count(const ops_keydata_t *key); +const unsigned char* ops_get_user_id(const ops_keydata_t *key, unsigned index); +ops_boolean_t ops_is_key_supported(const ops_keydata_t *key); +const ops_keydata_t* ops_keyring_get_key_by_index(const ops_keyring_t *keyring, int index); + +ops_user_id_t* ops_add_userid_to_keydata(ops_keydata_t* keydata, const ops_user_id_t* userid); +ops_packet_t* ops_add_packet_to_keydata(ops_keydata_t* keydata, const ops_packet_t* packet); +void ops_add_signed_userid_to_keydata(ops_keydata_t* keydata, const ops_user_id_t* userid, const ops_packet_t* packet); + +ops_boolean_t ops_add_selfsigned_userid_to_keydata(ops_keydata_t* keydata, ops_user_id_t* userid); + +ops_keydata_t *ops_keydata_new(void); +void ops_keydata_init(ops_keydata_t* keydata, const ops_content_tag_t type); + +#endif diff --git a/openpgpsdk/include/openpgpsdk/lists.h b/openpgpsdk/include/openpgpsdk/lists.h new file mode 100644 index 000000000..520b1d432 --- /dev/null +++ b/openpgpsdk/include/openpgpsdk/lists.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + */ + +#ifndef OPS_LISTS_H +#define OPS_LISTS_H + +/** ops_ulong_list_t */ +typedef struct + { + unsigned int size;/* num of array slots allocated */ + unsigned int used; /* num of array slots currently used */ + unsigned long *ulongs; + } ops_ulong_list_t; + +void ops_ulong_list_init(ops_ulong_list_t *list); +void ops_ulong_list_free(ops_ulong_list_t *list); +unsigned int ops_ulong_list_add(ops_ulong_list_t *list, unsigned long *ulong); + +#endif /* OPS_LISTS_H */ diff --git a/openpgpsdk/include/openpgpsdk/memory.h b/openpgpsdk/include/openpgpsdk/memory.h new file mode 100644 index 000000000..3fa098026 --- /dev/null +++ b/openpgpsdk/include/openpgpsdk/memory.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + */ + +#include +#include +#include "packet.h" + +#ifndef OPS_MEMORY_H +#define OPS_MEMORY_H + +/** ops_memory_t + */ +typedef struct ops_memory ops_memory_t; + +ops_memory_t *ops_memory_new(void); +void ops_memory_free(ops_memory_t *mem); +void ops_memory_init(ops_memory_t *mem,size_t initial_size); +void ops_memory_pad(ops_memory_t *mem,size_t length); +void ops_memory_add(ops_memory_t *mem,const unsigned char *src,size_t length); +void ops_memory_place_int(ops_memory_t *mem,unsigned offset,unsigned n, + size_t length); +void ops_memory_make_packet(ops_memory_t *out,ops_content_tag_t tag); +void ops_memory_clear(ops_memory_t *mem); +void ops_memory_release(ops_memory_t *mem); + +void ops_writer_set_memory(ops_create_info_t *info,ops_memory_t *mem); + +size_t ops_memory_get_length(const ops_memory_t *mem); +void *ops_memory_get_data(ops_memory_t *mem); + +#endif diff --git a/openpgpsdk/include/openpgpsdk/packet-parse.h b/openpgpsdk/include/openpgpsdk/packet-parse.h new file mode 100644 index 000000000..3f5b61a01 --- /dev/null +++ b/openpgpsdk/include/openpgpsdk/packet-parse.h @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + * Parser for OpenPGP packets - headers. + */ + +#ifndef OPS_PACKET_PARSE_H +#define OPS_PACKET_PARSE_H + +#include "types.h" +#include "packet.h" +#include "lists.h" + +/** ops_region_t */ +typedef struct ops_region + { + struct ops_region *parent; + unsigned length; + unsigned length_read; + unsigned last_read; /*!< length of last read, only valid in deepest child */ + ops_boolean_t indeterminate:1; + } ops_region_t; + +void ops_init_subregion(ops_region_t *subregion,ops_region_t *region); + +#if 0 +/** Return values for reader functions e.g. ops_packet_reader_t() */ +enum ops_reader_ret_t + { + OPS_R_OK =0, /*!< success */ + OPS_R_EOF =1, /*!< reached end of file, no data has been returned */ + OPS_R_EARLY_EOF =2, /*!< could not read the requested + number of bytes and either + OPS_RETURN_LENGTH was not set and at + least 1 byte was read, or there was + an abnormal end to the file (or + armoured block) */ + OPS_R_PARTIAL_READ =3, /*!< if OPS_RETURN_LENGTH is set and + the buffer was not filled */ + OPS_R_ERROR =4, /*!< if there was an error reading */ + }; +#endif + +/** ops_parse_callback_return_t */ +typedef enum + { + OPS_RELEASE_MEMORY, + OPS_KEEP_MEMORY, + OPS_FINISHED + } ops_parse_cb_return_t; + +typedef struct ops_parse_cb_info ops_parse_cb_info_t; + +typedef ops_parse_cb_return_t +ops_parse_cb_t(const ops_parser_content_t *content, + ops_parse_cb_info_t *cbinfo); + +typedef struct ops_parse_info ops_parse_info_t; +typedef struct ops_reader_info ops_reader_info_t; +typedef struct ops_crypt_info ops_crypt_info_t; + +/* + A reader MUST read at least one byte if it can, and should read up + to the number asked for. Whether it reads more for efficiency is + its own decision, but if it is a stacked reader it should never + read more than the length of the region it operates in (which it + would have to be given when it is stacked). + + If a read is short because of EOF, then it should return the short + read (obviously this will be zero on the second attempt, if not the + first). Because a reader is not obliged to do a full read, only a + zero return can be taken as an indication of EOF. + + If there is an error, then the callback should be notified, the + error stacked, and -1 should be returned. + + Note that although length is a size_t, a reader will never be asked + to read more than INT_MAX in one go. + + */ + +typedef int ops_reader_t(void *dest,size_t length,ops_error_t **errors, + ops_reader_info_t *rinfo,ops_parse_cb_info_t *cbinfo); + +typedef void ops_reader_destroyer_t(ops_reader_info_t *rinfo); + +ops_parse_info_t *ops_parse_info_new(void); +void ops_parse_info_delete(ops_parse_info_t *pinfo); +ops_error_t *ops_parse_info_get_errors(ops_parse_info_t *pinfo); +ops_crypt_t *ops_parse_get_decrypt(ops_parse_info_t *pinfo); + +void ops_parse_cb_set(ops_parse_info_t *pinfo,ops_parse_cb_t *cb,void *arg); +void ops_parse_cb_push(ops_parse_info_t *pinfo,ops_parse_cb_t *cb,void *arg); +void *ops_parse_cb_get_arg(ops_parse_cb_info_t *cbinfo); +void *ops_parse_cb_get_errors(ops_parse_cb_info_t *cbinfo); +void ops_reader_set(ops_parse_info_t *pinfo,ops_reader_t *reader,ops_reader_destroyer_t *destroyer,void *arg); +void ops_reader_push(ops_parse_info_t *pinfo,ops_reader_t *reader,ops_reader_destroyer_t *destroyer,void *arg); +void ops_reader_pop(ops_parse_info_t *pinfo); +void *ops_reader_get_arg_from_pinfo(ops_parse_info_t *pinfo); + +void *ops_reader_get_arg(ops_reader_info_t *rinfo); + +ops_parse_cb_return_t ops_parse_cb(const ops_parser_content_t *content, + ops_parse_cb_info_t *cbinfo); +ops_parse_cb_return_t ops_parse_stacked_cb(const ops_parser_content_t *content, + ops_parse_cb_info_t *cbinfo); +ops_reader_info_t *ops_parse_get_rinfo(ops_parse_info_t *pinfo); + +int ops_parse(ops_parse_info_t *parse_info); +int ops_parse_and_print_errors(ops_parse_info_t *parse_info); +int ops_parse_and_save_errs(ops_parse_info_t *parse_info,ops_ulong_list_t *errs); +int ops_parse_errs(ops_parse_info_t *parse_info,ops_ulong_list_t *errs); + +void ops_parse_and_validate(ops_parse_info_t *parse_info); + +/** Used to specify whether subpackets should be returned raw, parsed or ignored. + */ +enum ops_parse_type_t + { + OPS_PARSE_RAW, /*!< Callback Raw */ + OPS_PARSE_PARSED, /*!< Callback Parsed */ + OPS_PARSE_IGNORE, /*!< Don't callback */ + }; + +void ops_parse_options(ops_parse_info_t *pinfo,ops_content_tag_t tag, + ops_parse_type_t type); + +ops_boolean_t ops_limited_read(unsigned char *dest,size_t length, + ops_region_t *region,ops_error_t **errors, + ops_reader_info_t *rinfo, + ops_parse_cb_info_t *cbinfo); +ops_boolean_t ops_stacked_limited_read(unsigned char *dest,unsigned length, + ops_region_t *region, + ops_error_t **errors, + ops_reader_info_t *rinfo, + ops_parse_cb_info_t *cbinfo); +void ops_parse_hash_init(ops_parse_info_t *pinfo,ops_hash_algorithm_t type, + const unsigned char *keyid); +void ops_parse_hash_data(ops_parse_info_t *pinfo,const void *data, + size_t length); +void ops_parse_hash_finish(ops_parse_info_t *pinfo); +ops_hash_t *ops_parse_hash_find(ops_parse_info_t *pinfo, + const unsigned char keyid[OPS_KEY_ID_SIZE]); + +ops_reader_t ops_stacked_read; + +/* vim:set textwidth=120: */ +/* vim:set ts=8: */ + + +#endif diff --git a/openpgpsdk/include/openpgpsdk/packet-show-cast.h b/openpgpsdk/include/openpgpsdk/packet-show-cast.h new file mode 100644 index 000000000..b5c8b0ab4 --- /dev/null +++ b/openpgpsdk/include/openpgpsdk/packet-show-cast.h @@ -0,0 +1,38 @@ +/* Generated from packet-show.cast by ../../util/caster.pl, do not edit. */ + +#include "types.h" + +/* (line 4) char *show_packet_tag(ops_packet_tag_t packet_tag, packet_tag_map_t *packet_tag_map) -> char *ops_str_from_map(int packet_tag, ops_map_t *packet_tag_map) */ +char *ops_str_from_map(int packet_tag, ops_map_t *packet_tag_map); +#define show_packet_tag(packet_tag,packet_tag_map) ops_str_from_map(CHECKED_INSTANCE_OF(ops_packet_tag_t , packet_tag),CHECKED_INSTANCE_OF( packet_tag_map_t *, packet_tag_map)) +typedef char * show_packet_tag_t(ops_packet_tag_t , packet_tag_map_t *); + +/* (line 5) char *show_sig_type(ops_sig_type_t sig_type, sig_type_map_t *sig_type_map) -> char *ops_str_from_map(int sig_type, ops_map_t *sig_type_map) */ +char *ops_str_from_map(int sig_type, ops_map_t *sig_type_map); +#define show_sig_type(sig_type,sig_type_map) ops_str_from_map(CHECKED_INSTANCE_OF(ops_sig_type_t , sig_type),CHECKED_INSTANCE_OF( sig_type_map_t *, sig_type_map)) +typedef char * show_sig_type_t(ops_sig_type_t , sig_type_map_t *); + +/* (line 6) char *show_pka(ops_public_key_algorithm_t pka, public_key_algorithm_map_t *pka_map) -> char *ops_str_from_map(int pka, ops_map_t *pka_map) */ +char *ops_str_from_map(int pka, ops_map_t *pka_map); +#define show_pka(pka,pka_map) ops_str_from_map(CHECKED_INSTANCE_OF(ops_public_key_algorithm_t , pka),CHECKED_INSTANCE_OF( public_key_algorithm_map_t *, pka_map)) +typedef char * show_pka_t(ops_public_key_algorithm_t , public_key_algorithm_map_t *); + +/* (line 7) char *show_ss_type(ops_ss_type_t ss_type, ss_type_map_t *ss_type_map) -> char *ops_str_from_map(int ss_type, ops_map_t *ss_type_map) */ +char *ops_str_from_map(int ss_type, ops_map_t *ss_type_map); +#define show_ss_type(ss_type,ss_type_map) ops_str_from_map(CHECKED_INSTANCE_OF(ops_ss_type_t , ss_type),CHECKED_INSTANCE_OF( ss_type_map_t *, ss_type_map)) +typedef char * show_ss_type_t(ops_ss_type_t , ss_type_map_t *); + +/* (line 8) char *show_ss_rr_code(ops_ss_rr_code_t ss_rr_code, ss_rr_code_map_t *ss_rr_code_map) -> char *ops_str_from_map(int ss_rr_code, ops_map_t *ss_rr_code_map) */ +char *ops_str_from_map(int ss_rr_code, ops_map_t *ss_rr_code_map); +#define show_ss_rr_code(ss_rr_code,ss_rr_code_map) ops_str_from_map(CHECKED_INSTANCE_OF(ops_ss_rr_code_t , ss_rr_code),CHECKED_INSTANCE_OF( ss_rr_code_map_t *, ss_rr_code_map)) +typedef char * show_ss_rr_code_t(ops_ss_rr_code_t , ss_rr_code_map_t *); + +/* (line 9) char *show_hash_algorithm(unsigned char hash,+ops_map_t *hash_algorithm_map) -> char *ops_str_from_map(int hash,ops_map_t *hash_algorithm_map) */ +char *ops_str_from_map(int hash,ops_map_t *hash_algorithm_map); +#define show_hash_algorithm(hash) ops_str_from_map(CHECKED_INSTANCE_OF(unsigned char , hash),CHECKED_INSTANCE_OF(ops_map_t *, hash_algorithm_map)) +typedef char * show_hash_algorithm_t(unsigned char ); + +/* (line 10) char *show_symmetric_algorithm(unsigned char hash,+ops_map_t *symmetric_algorithm_map) -> char *ops_str_from_map(int hash,ops_map_t *symmetric_algorithm_map) */ +char *ops_str_from_map(int hash,ops_map_t *symmetric_algorithm_map); +#define show_symmetric_algorithm(hash) ops_str_from_map(CHECKED_INSTANCE_OF(unsigned char , hash),CHECKED_INSTANCE_OF(ops_map_t *, symmetric_algorithm_map)) +typedef char * show_symmetric_algorithm_t(unsigned char ); diff --git a/openpgpsdk/include/openpgpsdk/packet-show.h b/openpgpsdk/include/openpgpsdk/packet-show.h new file mode 100644 index 000000000..f8dc4050f --- /dev/null +++ b/openpgpsdk/include/openpgpsdk/packet-show.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + */ + +#ifndef OPS_PACKET_TO_TEXT_H +#define OPS_PACKET_TO_TEXT_H + +#ifndef OPS_PACKET_H +#include "packet.h" +#endif + +/** ops_list_t + */ +typedef struct + { + unsigned int size;/* num of array slots allocated */ + unsigned int used; /* num of array slots currently used */ + char **strings; + } ops_list_t; + +/** ops_text_t + */ +typedef struct + { + ops_list_t known; + ops_list_t unknown; + } ops_text_t; + +/** ops_bit_map_t + */ +typedef struct + { + unsigned char mask; + char *string; + } ops_bit_map_t; + +void ops_text_init(ops_text_t *text); +void ops_text_free(ops_text_t *text); + +const char *ops_show_packet_tag(ops_packet_tag_t packet_tag); +const char *ops_show_ss_type(ops_ss_type_t ss_type); + +const char *ops_show_sig_type(ops_sig_type_t sig_type); +const char *ops_show_pka(ops_public_key_algorithm_t pka); + +ops_text_t *ops_showall_ss_preferred_compression(ops_ss_preferred_compression_t ss_preferred_compression); +const char *ops_show_ss_preferred_compression(unsigned char octet); + +ops_text_t *ops_showall_ss_preferred_hash(ops_ss_preferred_hash_t ss_preferred_hash); +const char *ops_show_hash_algorithm(unsigned char octet); +const char *ops_show_symmetric_algorithm(unsigned char hash); + +ops_text_t *ops_showall_ss_preferred_ska(ops_ss_preferred_ska_t ss_preferred_ska); +const char *ops_show_ss_preferred_ska(unsigned char octet); + +const char *ops_show_ss_rr_code(ops_ss_rr_code_t ss_rr_code); + +ops_text_t *ops_showall_ss_features(ops_ss_features_t ss_features); + +ops_text_t *ops_showall_ss_key_flags(ops_ss_key_flags_t ss_key_flags); +const char *ops_show_ss_key_flag(unsigned char octet, ops_bit_map_t *map); + +ops_text_t *ops_showall_ss_key_server_prefs(ops_ss_key_server_prefs_t ss_key_server_prefs); +const char *ops_show_ss_key_server_prefs(unsigned char octet, + ops_bit_map_t *map); + +ops_text_t *ops_showall_ss_notation_data_flags(ops_ss_notation_data_t ss_notation_data); + +char *ops_str_from_map(int code, ops_map_t *map); + +/* vim:set textwidth=120: */ +/* vim:set ts=8: */ + +#endif /* OPS_PACKET_TO_TEXT_H */ diff --git a/openpgpsdk/include/openpgpsdk/packet.h b/openpgpsdk/include/openpgpsdk/packet.h new file mode 100644 index 000000000..5ee3540a0 --- /dev/null +++ b/openpgpsdk/include/openpgpsdk/packet.h @@ -0,0 +1,1053 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + * packet related headers. + */ + +#ifndef OPS_PACKET_H +#define OPS_PACKET_H + +#include "configure.h" + +#include +#include +#include +#include "types.h" +#include "errors.h" + +/** General-use structure for variable-length data + */ + +typedef struct + { + size_t len; + unsigned char *contents; + } ops_data_t; + +/************************************/ +/* Packet Tags - RFC4880, 4.2 */ +/************************************/ + +/** Packet Tag - Bit 7 Mask (this bit is always set). + * The first byte of a packet is the "Packet Tag". It always + * has bit 7 set. This is the mask for it. + * + * \see RFC4880 4.2 + */ +#define OPS_PTAG_ALWAYS_SET 0x80 + +/** Packet Tag - New Format Flag. + * Bit 6 of the Packet Tag is the packet format indicator. + * If it is set, the new format is used, if cleared the + * old format is used. + * + * \see RFC4880 4.2 + */ +#define OPS_PTAG_NEW_FORMAT 0x40 + + +/** Old Packet Format: Mask for content tag. + * In the old packet format bits 5 to 2 (including) + * are the content tag. This is the mask to apply + * to the packet tag. Note that you need to + * shift by #OPS_PTAG_OF_CONTENT_TAG_SHIFT bits. + * + * \see RFC4880 4.2 + */ +#define OPS_PTAG_OF_CONTENT_TAG_MASK 0x3c +/** Old Packet Format: Offset for the content tag. + * As described at #OPS_PTAG_OF_CONTENT_TAG_MASK the + * content tag needs to be shifted after being masked + * out from the Packet Tag. + * + * \see RFC4880 4.2 + */ +#define OPS_PTAG_OF_CONTENT_TAG_SHIFT 2 +/** Old Packet Format: Mask for length type. + * Bits 1 and 0 of the packet tag are the length type + * in the old packet format. + * + * See #ops_ptag_of_lt_t for the meaning of the values. + * + * \see RFC4880 4.2 + */ +#define OPS_PTAG_OF_LENGTH_TYPE_MASK 0x03 + + +/** Old Packet Format Lengths. + * Defines the meanings of the 2 bits for length type in the + * old packet format. + * + * \see RFC4880 4.2.1 + */ +typedef enum + { + OPS_PTAG_OF_LT_ONE_BYTE =0x00, /*!< Packet has a 1 byte length - header is 2 bytes long. */ + OPS_PTAG_OF_LT_TWO_BYTE =0x01, /*!< Packet has a 2 byte length - header is 3 bytes long. */ + OPS_PTAG_OF_LT_FOUR_BYTE =0x02, /*!< Packet has a 4 byte length - header is 5 bytes long. */ + OPS_PTAG_OF_LT_INDETERMINATE =0x03 /*!< Packet has a indeterminate length. */ + } ops_ptag_of_lt_t; + + +/** New Packet Format: Mask for content tag. + * In the new packet format the 6 rightmost bits + * are the content tag. This is the mask to apply + * to the packet tag. Note that you need to + * shift by #OPS_PTAG_NF_CONTENT_TAG_SHIFT bits. + * + * \see RFC4880 4.2 + */ +#define OPS_PTAG_NF_CONTENT_TAG_MASK 0x3f +/** New Packet Format: Offset for the content tag. + * As described at #OPS_PTAG_NF_CONTENT_TAG_MASK the + * content tag needs to be shifted after being masked + * out from the Packet Tag. + * + * \see RFC4880 4.2 + */ +#define OPS_PTAG_NF_CONTENT_TAG_SHIFT 0 + + + +/* PTag Content Tags */ +/***************************/ + +/** Package Tags (aka Content Tags) and signature subpacket types. + * This enumerates all rfc-defined packet tag values and the + * signature subpacket type values that we understand. + * + * \see RFC4880 4.3 + * \see RFC4880 5.2.3.1 + */ +enum ops_content_tag_t + { + OPS_PTAG_CT_RESERVED = 0, /*!< Reserved - a packet tag must not have this value */ + OPS_PTAG_CT_PK_SESSION_KEY = 1, /*!< Public-Key Encrypted Session Key Packet */ + OPS_PTAG_CT_SIGNATURE = 2, /*!< Signature Packet */ + OPS_PTAG_CT_SK_SESSION_KEY = 3, /*!< Symmetric-Key Encrypted Session Key Packet */ + OPS_PTAG_CT_ONE_PASS_SIGNATURE = 4, /*!< One-Pass Signature Packet */ + OPS_PTAG_CT_SECRET_KEY = 5, /*!< Secret Key Packet */ + OPS_PTAG_CT_PUBLIC_KEY = 6, /*!< Public Key Packet */ + OPS_PTAG_CT_SECRET_SUBKEY = 7, /*!< Secret Subkey Packet */ + OPS_PTAG_CT_COMPRESSED = 8, /*!< Compressed Data Packet */ + OPS_PTAG_CT_SE_DATA = 9, /*!< Symmetrically Encrypted Data Packet */ + OPS_PTAG_CT_MARKER =10, /*!< Marker Packet */ + OPS_PTAG_CT_LITERAL_DATA =11, /*!< Literal Data Packet */ + OPS_PTAG_CT_TRUST =12, /*!< Trust Packet */ + OPS_PTAG_CT_USER_ID =13, /*!< User ID Packet */ + OPS_PTAG_CT_PUBLIC_SUBKEY =14, /*!< Public Subkey Packet */ + OPS_PTAG_CT_RESERVED2 =15, /*!< reserved */ + OPS_PTAG_CT_RESERVED3 =16, /*!< reserved */ + OPS_PTAG_CT_USER_ATTRIBUTE =17, /*!< User Attribute Packet */ + OPS_PTAG_CT_SE_IP_DATA =18, /*!< Sym. Encrypted and Integrity Protected Data Packet */ + OPS_PTAG_CT_MDC =19, /*!< Modification Detection Code Packet */ + + OPS_PARSER_PTAG =0x100, /*!< Internal Use: The packet is the "Packet Tag" itself - used when + callback sends back the PTag. */ + OPS_PTAG_RAW_SS =0x101, /*!< Internal Use: content is raw sig subtag */ + OPS_PTAG_SS_ALL =0x102, /*!< Internal Use: select all subtags */ + OPS_PARSER_PACKET_END =0x103, + + /* signature subpackets (0x200-2ff) (type+0x200) */ + /* only those we can parse are listed here */ + OPS_PTAG_SIGNATURE_SUBPACKET_BASE =0x200, /*!< Base for signature subpacket types - All signature type + values are relative to this value. */ + OPS_PTAG_SS_CREATION_TIME =0x200+2, /*!< signature creation time */ + OPS_PTAG_SS_EXPIRATION_TIME =0x200+3, /*!< signature expiration time */ + + OPS_PTAG_SS_EXPORTABLE_CERTIFICATION =0x200+4, /*!< exportable certification */ + OPS_PTAG_SS_TRUST =0x200+5, /*!< trust signature */ + OPS_PTAG_SS_REGEXP =0x200+6, /*!< regular expression */ + OPS_PTAG_SS_REVOCABLE =0x200+7, /*!< revocable */ + OPS_PTAG_SS_KEY_EXPIRATION_TIME =0x200+9, /*!< key expiration time */ + OPS_PTAG_SS_RESERVED =0x200+10, /*!< reserved */ + OPS_PTAG_SS_PREFERRED_SKA =0x200+11, /*!< preferred symmetric algorithms */ + OPS_PTAG_SS_REVOCATION_KEY =0x200+12, /*!< revocation key */ + OPS_PTAG_SS_ISSUER_KEY_ID =0x200+16, /*!< issuer key ID */ + OPS_PTAG_SS_NOTATION_DATA =0x200+20, /*!< notation data */ + OPS_PTAG_SS_PREFERRED_HASH =0x200+21, /*!< preferred hash algorithms */ + OPS_PTAG_SS_PREFERRED_COMPRESSION =0x200+22, /*!< preferred compression algorithms */ + OPS_PTAG_SS_KEY_SERVER_PREFS =0x200+23, /*!< key server preferences */ + OPS_PTAG_SS_PREFERRED_KEY_SERVER =0x200+24, /*!< Preferred Key Server */ + OPS_PTAG_SS_PRIMARY_USER_ID =0x200+25, /*!< primary User ID */ + OPS_PTAG_SS_POLICY_URI =0x200+26, /*!< Policy URI */ + OPS_PTAG_SS_KEY_FLAGS =0x200+27, /*!< key flags */ + OPS_PTAG_SS_SIGNERS_USER_ID =0x200+28, /*!< Signer's User ID */ + OPS_PTAG_SS_REVOCATION_REASON =0x200+29, /*!< reason for revocation */ + OPS_PTAG_SS_FEATURES =0x200+30, /*!< features */ + OPS_PTAG_SS_SIGNATURE_TARGET =0x200+31, /*!< signature target */ + OPS_PTAG_SS_EMBEDDED_SIGNATURE=0x200+32, /*!< embedded signature */ + + OPS_PTAG_SS_USERDEFINED00 =0x200+100, /*!< internal or user-defined */ + OPS_PTAG_SS_USERDEFINED01 =0x200+101, + OPS_PTAG_SS_USERDEFINED02 =0x200+102, + OPS_PTAG_SS_USERDEFINED03 =0x200+103, + OPS_PTAG_SS_USERDEFINED04 =0x200+104, + OPS_PTAG_SS_USERDEFINED05 =0x200+105, + OPS_PTAG_SS_USERDEFINED06 =0x200+106, + OPS_PTAG_SS_USERDEFINED07 =0x200+107, + OPS_PTAG_SS_USERDEFINED08 =0x200+108, + OPS_PTAG_SS_USERDEFINED09 =0x200+109, + OPS_PTAG_SS_USERDEFINED10 =0x200+110, + + + /* pseudo content types */ + OPS_PTAG_CT_LITERAL_DATA_HEADER =0x300, + OPS_PTAG_CT_LITERAL_DATA_BODY =0x300+1, + OPS_PTAG_CT_SIGNATURE_HEADER =0x300+2, + OPS_PTAG_CT_SIGNATURE_FOOTER =0x300+3, + OPS_PTAG_CT_ARMOUR_HEADER =0x300+4, + OPS_PTAG_CT_ARMOUR_TRAILER =0x300+5, + OPS_PTAG_CT_SIGNED_CLEARTEXT_HEADER =0x300+6, + OPS_PTAG_CT_SIGNED_CLEARTEXT_BODY =0x300+7, + OPS_PTAG_CT_SIGNED_CLEARTEXT_TRAILER=0x300+8, + OPS_PTAG_CT_UNARMOURED_TEXT =0x300+9, + OPS_PTAG_CT_ENCRYPTED_SECRET_KEY =0x300+10, // In this case the algorithm specific fields will not be initialised + OPS_PTAG_CT_SE_DATA_HEADER =0x300+11, + OPS_PTAG_CT_SE_DATA_BODY =0x300+12, + OPS_PTAG_CT_SE_IP_DATA_HEADER =0x300+13, + OPS_PTAG_CT_SE_IP_DATA_BODY =0x300+14, + OPS_PTAG_CT_ENCRYPTED_PK_SESSION_KEY=0x300+15, + + /* commands to the callback */ + OPS_PARSER_CMD_GET_SK_PASSPHRASE =0x400, + OPS_PARSER_CMD_GET_SECRET_KEY =0x400+1, + + + /* Errors */ + OPS_PARSER_ERROR =0x500, /*!< Internal Use: Parser Error */ + OPS_PARSER_ERRCODE =0x500+1, /*! < Internal Use: Parser Error with errcode returned */ + }; + +/** Structure to hold one parse error string. */ +typedef struct + { + const char *error; /*!< error message. */ + } ops_parser_error_t; + +/** Structure to hold one error code */ +typedef struct + { + ops_errcode_t errcode; + } ops_parser_errcode_t; + +/** Structure to hold one packet tag. + * \see RFC4880 4.2 + */ +typedef struct + { + unsigned new_format; /*!< Whether this packet tag is new (true) or old format (false) */ + unsigned content_tag; /*!< content_tag value - See #ops_content_tag_t for meanings */ + ops_ptag_of_lt_t length_type; /*!< Length type (#ops_ptag_of_lt_t) - only if this packet tag is old format. Set to 0 if new format. */ + unsigned length; /*!< The length of the packet. This value is set when we read and compute the + length information, not at the same moment we create the packet tag structure. + Only defined if #length_read is set. */ /* XXX: Ben, is this correct? */ + unsigned position; /*!< The position (within the current reader) of the packet */ + } ops_ptag_t; + +/** Public Key Algorithm Numbers. + * OpenPGP assigns a unique Algorithm Number to each algorithm that is part of OpenPGP. + * + * This lists algorithm numbers for public key algorithms. + * + * \see RFC4880 9.1 + */ +typedef enum + { + OPS_PKA_RSA =1, /*!< RSA (Encrypt or Sign) */ + OPS_PKA_RSA_ENCRYPT_ONLY =2, /*!< RSA Encrypt-Only (deprecated - \see RFC4880 13.5) */ + OPS_PKA_RSA_SIGN_ONLY =3, /*!< RSA Sign-Only (deprecated - \see RFC4880 13.5) */ + OPS_PKA_ELGAMAL =16, /*!< Elgamal (Encrypt-Only) */ + OPS_PKA_DSA =17, /*!< DSA (Digital Signature Algorithm) */ + OPS_PKA_RESERVED_ELLIPTIC_CURVE =18, /*!< Reserved for Elliptic Curve */ + OPS_PKA_RESERVED_ECDSA =19, /*!< Reserved for ECDSA */ + OPS_PKA_ELGAMAL_ENCRYPT_OR_SIGN =20, /*!< Deprecated. */ + OPS_PKA_RESERVED_DH =21, /*!< Reserved for Diffie-Hellman (X9.42, as defined for IETF-S/MIME) */ + OPS_PKA_PRIVATE00 =100, /*!< Private/Experimental Algorithm */ + OPS_PKA_PRIVATE01 =101, /*!< Private/Experimental Algorithm */ + OPS_PKA_PRIVATE02 =102, /*!< Private/Experimental Algorithm */ + OPS_PKA_PRIVATE03 =103, /*!< Private/Experimental Algorithm */ + OPS_PKA_PRIVATE04 =104, /*!< Private/Experimental Algorithm */ + OPS_PKA_PRIVATE05 =105, /*!< Private/Experimental Algorithm */ + OPS_PKA_PRIVATE06 =106, /*!< Private/Experimental Algorithm */ + OPS_PKA_PRIVATE07 =107, /*!< Private/Experimental Algorithm */ + OPS_PKA_PRIVATE08 =108, /*!< Private/Experimental Algorithm */ + OPS_PKA_PRIVATE09 =109, /*!< Private/Experimental Algorithm */ + OPS_PKA_PRIVATE10 =110, /*!< Private/Experimental Algorithm */ + } ops_public_key_algorithm_t; + +/** Structure to hold one DSA public key parameters. + * + * \see RFC4880 5.5.2 + */ +typedef struct + { + BIGNUM *p; /*!< DSA prime p */ + BIGNUM *q; /*!< DSA group order q */ + BIGNUM *g; /*!< DSA group generator g */ + BIGNUM *y; /*!< DSA public key value y (= g^x mod p with x being the secret) */ + } ops_dsa_public_key_t; + +/** Structure to hold on RSA public key. + * + * \see RFC4880 5.5.2 + */ +typedef struct + { + BIGNUM *n; /*!< RSA public modulus n */ + BIGNUM *e; /*!< RSA public encryptiong exponent e */ + } ops_rsa_public_key_t; + +/** Structure to hold on ElGamal public key parameters. + * + * \see RFC4880 5.5.2 + */ +typedef struct + { + BIGNUM *p; /*!< ElGamal prime p */ + BIGNUM *g; /*!< ElGamal group generator g */ + BIGNUM *y; /*!< ElGamal public key value y (= g^x mod p with x being the secret) */ + } ops_elgamal_public_key_t; + +/** Union to hold public key parameters of any algorithm */ +typedef union + { + ops_dsa_public_key_t dsa; /*!< A DSA public key */ + ops_rsa_public_key_t rsa; /*!< An RSA public key */ + ops_elgamal_public_key_t elgamal; /*!< An ElGamal public key */ + } ops_public_key_union_t; + +/** Version. + * OpenPGP has two different protocol versions: version 3 and version 4. + * + * \see RFC4880 5.2 + */ +typedef enum + { + OPS_V2=2, /*contents to be a null-terminated list */ + } ops_ss_preferred_ska_t; + +/** Signature Subpacket : Preferrred Hash Algorithm */ +typedef struct + { + ops_data_t data; + } ops_ss_preferred_hash_t; + +/** Signature Subpacket : Preferred Compression */ +typedef struct + { + ops_data_t data; + } ops_ss_preferred_compression_t; + +/** Signature Subpacket : Key Flags */ +typedef struct + { + ops_data_t data; + } ops_ss_key_flags_t; + +/** Signature Subpacket : Key Server Preferences */ +typedef struct + { + ops_data_t data; + } ops_ss_key_server_prefs_t; + +/** Signature Subpacket : Features */ +typedef struct + { + ops_data_t data; + } ops_ss_features_t; + +/** Signature Subpacket : Signature Target */ +typedef struct + { + ops_public_key_algorithm_t pka_alg; + ops_hash_algorithm_t hash_alg; + ops_data_t hash; + } ops_ss_signature_target_t; + +/** Signature Subpacket : Embedded Signature */ +typedef struct + { + ops_data_t sig; + } ops_ss_embedded_signature_t; + +/** ops_packet_t */ + +typedef struct + { + size_t length; + unsigned char *raw; + } ops_packet_t; + +/** Types of Compression */ +typedef enum + { + OPS_C_NONE=0, + OPS_C_ZIP=1, + OPS_C_ZLIB=2, + OPS_C_BZIP2=3, + } ops_compression_type_t; + +/* unlike most structures, this will feed its data as a stream + * to the application instead of directly including it */ +/** ops_compressed_t */ +typedef struct + { + ops_compression_type_t type; + } ops_compressed_t; + +/** ops_one_pass_signature_t */ +typedef struct + { + unsigned char version; + ops_sig_type_t sig_type; + ops_hash_algorithm_t hash_algorithm; + ops_public_key_algorithm_t key_algorithm; + unsigned char keyid[OPS_KEY_ID_SIZE]; + ops_boolean_t nested; + } ops_one_pass_signature_t; + +/** Signature Subpacket : Primary User ID */ +typedef struct + { + ops_boolean_t primary_user_id; + } ops_ss_primary_user_id_t; + +/** Signature Subpacket : Regexp */ +typedef struct + { + char *text; + } ops_ss_regexp_t; + +/** Signature Subpacket : Policy URL */ +typedef struct + { + char *text; + } ops_ss_policy_url_t; + +/** Signature Subpacket : Preferred Key Server */ +typedef struct + { + char *text; + } ops_ss_preferred_key_server_t; + +/** Signature Subpacket : Revocation Key */ +typedef struct + { + unsigned char class; + unsigned char algid; + unsigned char fingerprint[20]; + } ops_ss_revocation_key_t; + +/** Signature Subpacket : Revocation Reason */ +typedef struct + { + unsigned char code; + char *text; + } ops_ss_revocation_reason_t; + +/** literal_data_type_t */ +typedef enum + { + OPS_LDT_BINARY='b', + OPS_LDT_TEXT='t', + OPS_LDT_UTF8='u', + OPS_LDT_LOCAL='l', + OPS_LDT_LOCAL2='1' + } ops_literal_data_type_t; + +/** ops_literal_data_header_t */ +typedef struct + { + ops_literal_data_type_t format; + char filename[256]; + time_t modification_time; + } ops_literal_data_header_t; + +/** ops_literal_data_body_t */ +typedef struct + { + unsigned length; + unsigned char data[8192]; + } ops_literal_data_body_t; + +/** ops_mdc_t */ +typedef struct + { + unsigned char data[20]; // size of SHA1 hash + } ops_mdc_t; + +/** ops_armoured_header_value_t */ +typedef struct + { + char *key; + char *value; + } ops_armoured_header_value_t; + +/** ops_headers_t */ +typedef struct + { + ops_armoured_header_value_t *headers; + unsigned nheaders; + } ops_headers_t; + +/** ops_armour_header_t */ +typedef struct + { + const char *type; + ops_headers_t headers; + } ops_armour_header_t; + +/** ops_armour_trailer_t */ +typedef struct + { + const char *type; + } ops_armour_trailer_t; + +/** ops_signed_cleartext_header_t */ +typedef struct + { + ops_headers_t headers; + } ops_signed_cleartext_header_t; + +/** ops_signed_cleartext_body_t */ +typedef struct + { + unsigned length; + unsigned char data[8192]; // \todo fix hard-coded value? + } ops_signed_cleartext_body_t; + +/** ops_signed_cleartext_trailer_t */ +typedef struct + { + struct _ops_hash_t *hash; /*!< This will not have been finalised, but will have seen all the cleartext data in canonical form */ + } ops_signed_cleartext_trailer_t; + +/** ops_unarmoured_text_t */ +typedef struct + { + unsigned length; + unsigned char *data; + } ops_unarmoured_text_t; + +typedef enum + { + SE_IP_DATA_VERSION=1 + } ops_se_ip_data_version_t; + +typedef enum + { + OPS_PKSK_V3=3 + } ops_pk_session_key_version_t; + +/** ops_pk_session_key_parameters_rsa_t */ +typedef struct + { + BIGNUM *encrypted_m; + BIGNUM *m; + } ops_pk_session_key_parameters_rsa_t; + +/** ops_pk_session_key_parameters_elgamal_t */ +typedef struct + { + BIGNUM *g_to_k; + BIGNUM *encrypted_m; + } ops_pk_session_key_parameters_elgamal_t; + +/** ops_pk_session_key_parameters_t */ +typedef union + { + ops_pk_session_key_parameters_rsa_t rsa; + ops_pk_session_key_parameters_elgamal_t elgamal; + } ops_pk_session_key_parameters_t; + +/** ops_pk_session_key_t */ +typedef struct + { + ops_pk_session_key_version_t version; + unsigned char key_id[OPS_KEY_ID_SIZE]; + ops_public_key_algorithm_t algorithm; + ops_pk_session_key_parameters_t parameters; + ops_symmetric_algorithm_t symmetric_algorithm; + unsigned char key[OPS_MAX_KEY_SIZE]; + unsigned short checksum; + } ops_pk_session_key_t; + +/** ops_secret_key_passphrase_t */ +typedef struct + { + const ops_secret_key_t *secret_key; + char **passphrase; /* point somewhere that gets filled in to work around constness of content */ + } ops_secret_key_passphrase_t; + +typedef enum + { + OPS_SE_IP_V1=1 + } ops_se_ip_version_t; + +/** ops_se_ip_data_header_t */ +typedef struct + { + ops_se_ip_version_t version; + } ops_se_ip_data_header_t; + +/** ops_se_ip_data_body_t */ +typedef struct + { + unsigned length; + unsigned char* data; // \todo remember to free this + } ops_se_ip_data_body_t; + +/** ops_se_data_body_t */ +typedef struct + { + unsigned length; + unsigned char data[8192]; // \todo parameterise this! + } ops_se_data_body_t; + +/** ops_get_secret_key_t */ +typedef struct + { + const ops_secret_key_t **secret_key; + const ops_pk_session_key_t *pk_session_key; + } ops_get_secret_key_t; + +/** ops_parser_union_content_t */ +typedef union + { + ops_parser_error_t error; + ops_parser_errcode_t errcode; + ops_ptag_t ptag; + ops_public_key_t public_key; + ops_trust_t trust; + ops_user_id_t user_id; + ops_user_attribute_t user_attribute; + ops_signature_t signature; + ops_ss_raw_t ss_raw; + ops_ss_trust_t ss_trust; + ops_ss_revocable_t ss_revocable; + ops_ss_time_t ss_time; + ops_ss_key_id_t ss_issuer_key_id; + ops_ss_notation_data_t ss_notation_data; + ops_packet_t packet; + ops_compressed_t compressed; + ops_one_pass_signature_t one_pass_signature; + ops_ss_preferred_ska_t ss_preferred_ska; + ops_ss_preferred_hash_t ss_preferred_hash; + ops_ss_preferred_compression_t ss_preferred_compression; + ops_ss_key_flags_t ss_key_flags; + ops_ss_key_server_prefs_t ss_key_server_prefs; + ops_ss_primary_user_id_t ss_primary_user_id; + ops_ss_regexp_t ss_regexp; + ops_ss_policy_url_t ss_policy_url; + ops_ss_preferred_key_server_t ss_preferred_key_server; + ops_ss_revocation_key_t ss_revocation_key; + ops_ss_userdefined_t ss_userdefined; + ops_ss_unknown_t ss_unknown; + ops_literal_data_header_t literal_data_header; + ops_literal_data_body_t literal_data_body; + ops_mdc_t mdc; + ops_ss_features_t ss_features; + ops_ss_signature_target_t ss_signature_target; + ops_ss_embedded_signature_t ss_embedded_signature; + ops_ss_revocation_reason_t ss_revocation_reason; + ops_secret_key_t secret_key; + ops_user_id_t ss_signers_user_id; + ops_armour_header_t armour_header; + ops_armour_trailer_t armour_trailer; + ops_signed_cleartext_header_t signed_cleartext_header; + ops_signed_cleartext_body_t signed_cleartext_body; + ops_signed_cleartext_trailer_t signed_cleartext_trailer; + ops_unarmoured_text_t unarmoured_text; + ops_pk_session_key_t pk_session_key; + ops_secret_key_passphrase_t secret_key_passphrase; + ops_se_ip_data_header_t se_ip_data_header; + ops_se_ip_data_body_t se_ip_data_body; + ops_se_data_body_t se_data_body; + ops_get_secret_key_t get_secret_key; + } ops_parser_content_union_t; + +/** ops_parser_content_t */ +struct ops_parser_content_t + { + ops_content_tag_t tag; + unsigned char critical; /* for signature subpackets */ + ops_parser_content_union_t content; + }; + +/** ops_fingerprint_t */ +typedef struct + { + unsigned char fingerprint[20]; + unsigned length; + } ops_fingerprint_t; + +void ops_init(void); +void ops_finish(void); +void ops_keyid(unsigned char keyid[OPS_KEY_ID_SIZE], + const ops_public_key_t *key); +void ops_fingerprint(ops_fingerprint_t *fp,const ops_public_key_t *key); +void ops_public_key_free(ops_public_key_t *key); +void ops_user_id_free(ops_user_id_t *id); +void ops_user_attribute_free(ops_user_attribute_t *att); +void ops_signature_free(ops_signature_t *sig); +void ops_trust_free(ops_trust_t *trust); +void ops_ss_preferred_ska_free(ops_ss_preferred_ska_t *ss_preferred_ska); +void ops_ss_preferred_hash_free(ops_ss_preferred_hash_t *ss_preferred_hash); +void ops_ss_preferred_compression_free(ops_ss_preferred_compression_t *ss_preferred_compression); +void ops_ss_key_flags_free(ops_ss_key_flags_t *ss_key_flags); +void ops_ss_key_server_prefs_free(ops_ss_key_server_prefs_t *ss_key_server_prefs); +void ops_ss_features_free(ops_ss_features_t *ss_features); +void ops_ss_notation_data_free(ops_ss_notation_data_t *ss_notation_data); +void ops_ss_policy_url_free(ops_ss_policy_url_t *ss_policy_url); +void ops_ss_preferred_key_server_free(ops_ss_preferred_key_server_t *ss_preferred_key_server); +void ops_ss_regexp_free(ops_ss_regexp_t *ss_regexp); +void ops_ss_userdefined_free(ops_ss_userdefined_t *ss_userdefined); +void ops_ss_reserved_free(ops_ss_unknown_t *ss_unknown); +void ops_ss_revocation_reason_free(ops_ss_revocation_reason_t *ss_revocation_reason); +void ops_ss_signature_target_free(ops_ss_signature_target_t *ss_signature_target); +void ops_ss_embedded_signature_free(ops_ss_embedded_signature_t *ss_embedded_signature); + +void ops_packet_free(ops_packet_t *packet); +void ops_parser_content_free(ops_parser_content_t *c); +void ops_secret_key_free(ops_secret_key_t *key); +void ops_pk_session_key_free(ops_pk_session_key_t *sk); + +/* vim:set textwidth=120: */ +/* vim:set ts=8: */ + +#endif diff --git a/openpgpsdk/include/openpgpsdk/random.h b/openpgpsdk/include/openpgpsdk/random.h new file mode 100644 index 000000000..833094353 --- /dev/null +++ b/openpgpsdk/include/openpgpsdk/random.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WIN32 +#include +#else +#include +#endif + +void ops_random(void *dest,size_t length); diff --git a/openpgpsdk/include/openpgpsdk/readerwriter.h b/openpgpsdk/include/openpgpsdk/readerwriter.h new file mode 100644 index 000000000..6961bafef --- /dev/null +++ b/openpgpsdk/include/openpgpsdk/readerwriter.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OPS_READERWRITER_H__ +#define __OPS_READERWRITER_H__ + +#include +#include + + +void ops_reader_set_fd(ops_parse_info_t *pinfo,int fd); +void ops_reader_set_memory(ops_parse_info_t *pinfo,const void *buffer, + size_t length); + +// Do a sum mod 65536 of all bytes read (as needed for secret keys) +void ops_reader_push_sum16(ops_parse_info_t *pinfo); +unsigned short ops_reader_pop_sum16(ops_parse_info_t *pinfo); + +void ops_reader_push_se_ip_data(ops_parse_info_t *pinfo, ops_crypt_t *decrypt, + ops_region_t *region); +void ops_reader_pop_se_ip_data(ops_parse_info_t* pinfo); + +// +ops_boolean_t ops_write_mdc(const unsigned char *hashed, + ops_create_info_t* info); +ops_boolean_t ops_write_se_ip_pktset(const unsigned char *data, + const unsigned int len, + ops_crypt_t *crypt, + ops_create_info_t *info); +void ops_writer_push_encrypt_crypt(ops_create_info_t *cinfo, + ops_crypt_t *crypt); +void ops_writer_push_encrypt_se_ip(ops_create_info_t *cinfo, + const ops_keydata_t *pub_key); +// Secret Key checksum + +void ops_push_skey_checksum_writer(ops_create_info_t *cinfo, ops_secret_key_t *skey); +ops_boolean_t ops_pop_skey_checksum_writer(ops_create_info_t *cinfo); + + +// memory writing +void ops_setup_memory_write(ops_create_info_t **cinfo, ops_memory_t **mem, size_t bufsz); +void ops_teardown_memory_write(ops_create_info_t *cinfo, ops_memory_t *mem); + +// memory reading +void ops_setup_memory_read(ops_parse_info_t **pinfo, ops_memory_t *mem, + void* arg, + ops_parse_cb_return_t callback(const ops_parser_content_t *, ops_parse_cb_info_t *),ops_boolean_t accumulate); +void ops_teardown_memory_read(ops_parse_info_t *pinfo, ops_memory_t *mem); + +// file writing +int ops_setup_file_write(ops_create_info_t **cinfo, const char* filename, ops_boolean_t allow_overwrite); +void ops_teardown_file_write(ops_create_info_t *cinfo, int fd); + +// file appending +int ops_setup_file_append(ops_create_info_t **cinfo, const char* filename); +void ops_teardown_file_append(ops_create_info_t *cinfo, int fd); + +// file reading +int ops_setup_file_read(ops_parse_info_t **pinfo, const char *filename, void* arg, + ops_parse_cb_return_t callback(const ops_parser_content_t *, ops_parse_cb_info_t *), ops_boolean_t accumulate); +void ops_teardown_file_read(ops_parse_info_t *pinfo, int fd); + +ops_boolean_t ops_reader_set_accumulate(ops_parse_info_t* pinfo, ops_boolean_t state); + +// useful callbacks +ops_parse_cb_return_t +callback_literal_data(const ops_parser_content_t *content_,ops_parse_cb_info_t *cbinfo); +ops_parse_cb_return_t +callback_pk_session_key(const ops_parser_content_t *content_,ops_parse_cb_info_t *cbinfo); +ops_parse_cb_return_t +callback_cmd_get_secret_key(const ops_parser_content_t *content_,ops_parse_cb_info_t *cbinfo); +ops_parse_cb_return_t +callback_cmd_get_passphrase_from_cmdline(const ops_parser_content_t *content_,ops_parse_cb_info_t *cbinfo); + +#endif /*OPS_READERWRITER_H__*/ diff --git a/openpgpsdk/include/openpgpsdk/signature.h b/openpgpsdk/include/openpgpsdk/signature.h new file mode 100644 index 000000000..c555d6aca --- /dev/null +++ b/openpgpsdk/include/openpgpsdk/signature.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + */ + +#ifndef OPS_SIGNATURE_H +#define OPS_SIGNATURE_H + +#include "packet.h" +#include "util.h" +#include "create.h" + +typedef struct ops_create_signature ops_create_signature_t; + +ops_create_signature_t *ops_create_signature_new(void); +void ops_create_signature_delete(ops_create_signature_t *sig); + +ops_boolean_t +ops_check_user_id_certification_signature(const ops_public_key_t *key, + const ops_user_id_t *id, + const ops_signature_t *sig, + const ops_public_key_t *signer, + const unsigned char *raw_packet); +ops_boolean_t +ops_check_user_attribute_certification_signature(const ops_public_key_t *key, + const ops_user_attribute_t *attribute, + const ops_signature_t *sig, + const ops_public_key_t *signer, + const unsigned char *raw_packet); +ops_boolean_t +ops_check_subkey_signature(const ops_public_key_t *key, + const ops_public_key_t *subkey, + const ops_signature_t *sig, + const ops_public_key_t *signer, + const unsigned char *raw_packet); +ops_boolean_t +ops_check_direct_signature(const ops_public_key_t *key, + const ops_signature_t *sig, + const ops_public_key_t *signer, + const unsigned char *raw_packet); +ops_boolean_t +ops_check_hash_signature(ops_hash_t *hash, + const ops_signature_t *sig, + const ops_public_key_t *signer); +void ops_signature_start_key_signature(ops_create_signature_t *sig, + const ops_public_key_t *key, + const ops_user_id_t *id, + ops_sig_type_t type); +void ops_signature_start_cleartext_signature(ops_create_signature_t *sig, + const ops_secret_key_t *key, + const ops_hash_algorithm_t hash, + const ops_sig_type_t type); +void ops_signature_start_message_signature(ops_create_signature_t *sig, + const ops_secret_key_t *key, + const ops_hash_algorithm_t hash, + const ops_sig_type_t type); + +void ops_signature_add_data(ops_create_signature_t *sig,const void *buf, + size_t length); +ops_hash_t *ops_signature_get_hash(ops_create_signature_t *sig); +ops_boolean_t ops_signature_hashed_subpackets_end(ops_create_signature_t *sig); +ops_boolean_t ops_write_signature(ops_create_signature_t *sig,const ops_public_key_t *key, + const ops_secret_key_t *skey, ops_create_info_t *opt); +ops_boolean_t ops_signature_add_creation_time(ops_create_signature_t *sig,time_t when); +ops_boolean_t ops_signature_add_issuer_key_id(ops_create_signature_t *sig, + const unsigned char keyid[OPS_KEY_ID_SIZE]); +void ops_signature_add_primary_user_id(ops_create_signature_t *sig, + ops_boolean_t primary); + +// Standard Interface +ops_boolean_t ops_sign_file_as_cleartext(const char* input_filename, const char* output_filename, const ops_secret_key_t *skey, const ops_boolean_t overwrite); +ops_boolean_t ops_sign_buf_as_cleartext(const char* input, const size_t len, ops_memory_t** output, const ops_secret_key_t *skey); +ops_boolean_t ops_sign_file(const char* input_filename, const char* output_filename, const ops_secret_key_t *skey, const ops_boolean_t use_armour, const ops_boolean_t overwrite); +ops_memory_t * ops_sign_buf(const void* input, const size_t input_len, const ops_sig_type_t sig_type, const ops_secret_key_t *skey, const ops_boolean_t use_armour); + +#endif diff --git a/openpgpsdk/include/openpgpsdk/std_print.h b/openpgpsdk/include/openpgpsdk/std_print.h new file mode 100644 index 000000000..f6ae7dba1 --- /dev/null +++ b/openpgpsdk/include/openpgpsdk/std_print.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + */ + +#ifndef OPS_STD_PRINT_H +#define OPS_STD_PRINT_H + +#include "openpgpsdk/packet.h" +#include "openpgpsdk/packet-parse.h" +#include "openpgpsdk/keyring.h" + +void print_bn( const char *name, + const BIGNUM *bn); +void ops_print_pk_session_key(ops_content_tag_t tag, + const ops_pk_session_key_t *key); +void ops_print_public_keydata(const ops_keydata_t *key); + +void ops_print_public_keydata_verbose(const ops_keydata_t *key); +void ops_print_public_key(const ops_public_key_t *pkey); + +void ops_print_secret_keydata(const ops_keydata_t *key); +void ops_print_secret_keydata_verbose(const ops_keydata_t *key); +//void ops_print_secret_key(const ops_content_tag_t type, const ops_secret_key_t* skey); +int ops_print_packet(const ops_parser_content_t *content_); +void ops_list_packets(char *filename, ops_boolean_t armour, ops_keyring_t* pubring, ops_parse_cb_t* cb_get_passphrase); + +#endif diff --git a/openpgpsdk/include/openpgpsdk/streamwriter.h b/openpgpsdk/include/openpgpsdk/streamwriter.h new file mode 100644 index 000000000..c9bd51709 --- /dev/null +++ b/openpgpsdk/include/openpgpsdk/streamwriter.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OPS_STREAMWRITER_H__ +#define __OPS_STREAMWRITER_H__ + +#include + +void ops_writer_push_stream_encrypt_se_ip(ops_create_info_t *cinfo, + const ops_key_data_t *pub_key); + +#endif /*__OPS_STREAMWRITER_H__*/ diff --git a/openpgpsdk/include/openpgpsdk/types.h b/openpgpsdk/include/openpgpsdk/types.h new file mode 100644 index 000000000..c928abf9b --- /dev/null +++ b/openpgpsdk/include/openpgpsdk/types.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + */ + +#ifndef OPS_TYPES_H +#define OPS_TYPES_H + +/** Special type for intermediate function casting, avoids warnings on + some platforms +*/ +typedef void (*ops_void_fptr)(void); +#define ops_fcast(f) ((ops_void_fptr)f) + +/** ops_map_t + */ +typedef struct + { + int type; + char *string; + } ops_map_t; + +/** Boolean type */ +typedef unsigned ops_boolean_t; + +/** ops_content_tag_t */ +typedef enum ops_content_tag_t ops_content_tag_t; + +typedef struct _ops_crypt_t ops_crypt_t; + +/** ops_hash_t */ +typedef struct _ops_hash_t ops_hash_t; + +/** + keep both ops_content_tag_t and ops_packet_tag_t because we might + want to introduce some bounds checking i.e. is this really a valid value + for a packet tag? +*/ +typedef enum ops_content_tag_t ops_packet_tag_t; +/** SS types are a subset of all content types. +*/ +typedef enum ops_content_tag_t ops_ss_type_t; +/* typedef enum ops_sig_type_t ops_sig_type_t; */ + +/** Revocation Reason type */ +typedef unsigned char ops_ss_rr_code_t; + +/** ops_parse_type_t */ +typedef enum ops_parse_type_t ops_parse_type_t; + +/** ops_parser_content_t */ +typedef struct ops_parser_content_t ops_parser_content_t; + +/** Reader Flags */ +/* +typedef enum + { + OPS_RETURN_LENGTH=1, + } ops_reader_flags_t; +typedef enum ops_reader_ret_t ops_reader_ret_t; +*/ + +/** Writer flags */ +typedef enum + { + OPS_WF_DUMMY, + } ops_writer_flags_t; +/** ops_writer_ret_t */ +typedef enum ops_writer_ret_t ops_writer_ret_t; + +/** + * \ingroup Create + * Contains the required information about how to write + */ +typedef struct ops_create_info ops_create_info_t; + +#endif diff --git a/openpgpsdk/include/openpgpsdk/util.h b/openpgpsdk/include/openpgpsdk/util.h new file mode 100644 index 000000000..88d7b10a8 --- /dev/null +++ b/openpgpsdk/include/openpgpsdk/util.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + */ + +#ifndef OPS_UTIL_H +#define OPS_UTIL_H + +#include "openpgpsdk/types.h" +#include "openpgpsdk/create.h" +#include "openpgpsdk/packet-parse.h" +#include + +#define ops_false 0 +#define ops_true 1 + +void hexdump(const unsigned char *src,size_t length); + +/* + * These macros code ensures that you are casting what you intend to cast. + * It works because in "a ? b : c", b and c must have the same type. + * This is a copy of the macro defined in openssl/asn1.h. + */ +#ifndef CHECKED_PTR_OF +#define CHECKED_PTR_OF(type, p) ((void*) (1 ? p : (type *)0)) +#endif +#define CHECKED_INSTANCE_OF(type, p) (1 ? p : (type)0) +#define DECONST(type,p) ((type *)CHECKED_PTR_OF(const type, p)) + +/* number of elements in an array */ +#define OPS_ARRAY_SIZE(a) (sizeof(a)/sizeof(*(a))) + +void *ops_mallocz(size_t n); + +#endif diff --git a/openpgpsdk/include/openpgpsdk/validate.h b/openpgpsdk/include/openpgpsdk/validate.h new file mode 100644 index 000000000..39755328c --- /dev/null +++ b/openpgpsdk/include/openpgpsdk/validate.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +typedef struct + { + unsigned int valid_count; + ops_signature_info_t * valid_sigs; + unsigned int invalid_count; + ops_signature_info_t * invalid_sigs; + unsigned int unknown_signer_count; + ops_signature_info_t * unknown_sigs; + } ops_validate_result_t; + +void ops_validate_result_free(ops_validate_result_t *result); + +ops_boolean_t ops_validate_key_signatures(ops_validate_result_t *result, + const ops_keydata_t* keydata, + const ops_keyring_t *ring, + ops_parse_cb_return_t cb (const ops_parser_content_t *, ops_parse_cb_info_t *)); +ops_boolean_t ops_validate_all_signatures(ops_validate_result_t *result, + const ops_keyring_t *ring, + ops_parse_cb_return_t (const ops_parser_content_t *, ops_parse_cb_info_t *)); + +void ops_keydata_reader_set(ops_parse_info_t *pinfo, + const ops_keydata_t *key); + +typedef struct + { + const ops_keydata_t *key; + unsigned packet; + unsigned offset; + } validate_reader_arg_t; + +/** Struct used with the validate_key_cb callback */ +typedef struct + { + ops_public_key_t pkey; + ops_public_key_t subkey; + ops_secret_key_t skey; + enum + { + ATTRIBUTE=1, + ID, + } last_seen; + ops_user_id_t user_id; + ops_user_attribute_t user_attribute; + unsigned char hash[OPS_MAX_HASH_SIZE]; + const ops_keyring_t *keyring; + validate_reader_arg_t *rarg; + ops_validate_result_t *result; + ops_parse_cb_return_t (*cb_get_passphrase) (const ops_parser_content_t *, ops_parse_cb_info_t *); + } validate_key_cb_arg_t; + +/** Struct use with the validate_data_cb callback */ +typedef struct + { + enum + { + LITERAL_DATA, + SIGNED_CLEARTEXT + } use; /* + +ops_boolean_t ops_writer_push_clearsigned(ops_create_info_t *info, + ops_create_signature_t *sig); +void ops_writer_push_armoured_message(ops_create_info_t *info); +ops_boolean_t ops_writer_switch_to_armoured_signature(ops_create_info_t *info); + +void ops_writer_push_armoured(ops_create_info_t *info, ops_armor_type_t type); + +// EOF diff --git a/openpgpsdk/src/accumulate.c b/openpgpsdk/src/accumulate.c new file mode 100644 index 000000000..858b5933d --- /dev/null +++ b/openpgpsdk/src/accumulate.c @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + */ + +#include +#include +#include +#include +#include "keyring_local.h" +#include "parse_local.h" +#include +#include +#include +#include + +#include + +typedef struct + { + ops_keyring_t *keyring; + } accumulate_arg_t; + +/** + * \ingroup Core_Callbacks + */ +static ops_parse_cb_return_t +accumulate_cb(const ops_parser_content_t *content_,ops_parse_cb_info_t *cbinfo) + { + accumulate_arg_t *arg=ops_parse_cb_get_arg(cbinfo); + const ops_parser_content_union_t *content=&content_->content; + ops_keyring_t *keyring=arg->keyring; + ops_keydata_t *cur=NULL; + const ops_public_key_t *pkey; + + if(keyring->nkeys >= 0) + cur=&keyring->keys[keyring->nkeys]; + + switch(content_->tag) + { + case OPS_PTAG_CT_PUBLIC_KEY: + case OPS_PTAG_CT_SECRET_KEY: + case OPS_PTAG_CT_ENCRYPTED_SECRET_KEY: + // printf("New key\n"); + ++keyring->nkeys; + EXPAND_ARRAY(keyring,keys); + + if(content_->tag == OPS_PTAG_CT_PUBLIC_KEY) + pkey=&content->public_key; + else + pkey=&content->secret_key.public_key; + + memset(&keyring->keys[keyring->nkeys],'\0', + sizeof keyring->keys[keyring->nkeys]); + + ops_keyid(keyring->keys[keyring->nkeys].key_id,pkey); + ops_fingerprint(&keyring->keys[keyring->nkeys].fingerprint,pkey); + + keyring->keys[keyring->nkeys].type=content_->tag; + + if(content_->tag == OPS_PTAG_CT_PUBLIC_KEY) + keyring->keys[keyring->nkeys].key.pkey=*pkey; + else + keyring->keys[keyring->nkeys].key.skey=content->secret_key; + return OPS_KEEP_MEMORY; + + case OPS_PTAG_CT_USER_ID: + // printf("User ID: %s\n",content->user_id.user_id); + if (!cur) + { + OPS_ERROR(cbinfo->errors,OPS_E_P_NO_USERID, "No user id found"); + return OPS_KEEP_MEMORY; + } + // assert(cur); + ops_add_userid_to_keydata(cur, &content->user_id); + return OPS_KEEP_MEMORY; + + case OPS_PARSER_PACKET_END: + if(!cur) + return OPS_RELEASE_MEMORY; + ops_add_packet_to_keydata(cur, &content->packet); + return OPS_KEEP_MEMORY; + + case OPS_PARSER_ERROR: + fprintf(stderr,"Error: %s\n",content->error.error); + assert(0); + break; + + case OPS_PARSER_ERRCODE: + switch(content->errcode.errcode) + { + default: + fprintf(stderr,"parse error: %s\n", + ops_errcode(content->errcode.errcode)); + //assert(0); + } + break; + + default: + break; + } + + // XXX: we now exclude so many things, we should either drop this or + // do something to pass on copies of the stuff we keep + return ops_parse_stacked_cb(content_,cbinfo); + } + +/** + * \ingroup Core_Parse + * + * Parse packets from an input stream until EOF or error. + * + * Key data found in the parsed data is added to #keyring. + * + * \param keyring Pointer to an existing keyring + * \param parse_info Options to use when parsing +*/ + +int ops_parse_and_accumulate(ops_keyring_t *keyring, + ops_parse_info_t *parse_info) + { + int rtn; + + accumulate_arg_t arg; + + assert(!parse_info->rinfo.accumulate); + + memset(&arg,'\0',sizeof arg); + + arg.keyring=keyring; + /* Kinda weird, but to do with counting, and we put it back after */ + --keyring->nkeys; + + ops_parse_cb_push(parse_info,accumulate_cb,&arg); + + parse_info->rinfo.accumulate=ops_true; + + rtn=ops_parse(parse_info); + ++keyring->nkeys; + + return rtn; + } + +static void dump_one_keydata(const ops_keydata_t *key) + { + unsigned n; + + printf("Key ID: "); + hexdump(key->key_id,8); + + printf("\nFingerpint: "); + hexdump(key->fingerprint.fingerprint,key->fingerprint.length); + + printf("\n\nUIDs\n====\n\n"); + for(n=0 ; n < key->nuids ; ++n) + printf("%s\n",key->uids[n].user_id); + + printf("\nPackets\n=======\n"); + for(n=0 ; n < key->npackets ; ++n) + { + printf("\n%03d: ",n); + hexdump(key->packets[n].raw,key->packets[n].length); + } + printf("\n\n"); + } + +// XXX: not a maintained part of the API - use ops_keyring_list() +/** ops_dump_keyring +*/ +void ops_dump_keyring(const ops_keyring_t *keyring) + { + int n; + + for(n=0 ; n < keyring->nkeys ; ++n) + dump_one_keydata(&keyring->keys[n]); + } diff --git a/openpgpsdk/src/compress.c b/openpgpsdk/src/compress.c new file mode 100644 index 000000000..c214a1b3f --- /dev/null +++ b/openpgpsdk/src/compress.c @@ -0,0 +1,405 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include "parse_local.h" +#include + +#define DECOMPRESS_BUFFER 1024 + +typedef struct + { + ops_compression_type_t type; + ops_region_t *region; + unsigned char in[DECOMPRESS_BUFFER]; + unsigned char out[DECOMPRESS_BUFFER]; + z_stream zstream; // ZIP and ZLIB + size_t offset; + int inflate_ret; + } z_decompress_arg_t; + +typedef struct + { + ops_compression_type_t type; + ops_region_t *region; + char in[DECOMPRESS_BUFFER]; + char out[DECOMPRESS_BUFFER]; + bz_stream bzstream; // BZIP2 + size_t offset; + int inflate_ret; + } bz_decompress_arg_t; + +typedef struct + { + z_stream stream; + unsigned char *src; + unsigned char *dst; + } compress_arg_t; + +// \todo remove code duplication between this and bzip2_compressed_data_reader +static int zlib_compressed_data_reader(void *dest,size_t length, + ops_error_t **errors, + ops_reader_info_t *rinfo, + ops_parse_cb_info_t *cbinfo) + { + z_decompress_arg_t *arg=ops_reader_get_arg(rinfo); + assert(arg->type==OPS_C_ZIP || arg->type==OPS_C_ZLIB); + + //ops_parser_content_t content; + int saved=length; + + if(/*arg->region->indeterminate && */ arg->inflate_ret == Z_STREAM_END + && arg->zstream.next_out == &arg->out[arg->offset]) + return 0; + + if(arg->region->length_read == arg->region->length) + { + if(arg->inflate_ret != Z_STREAM_END) + OPS_ERROR(cbinfo->errors, OPS_E_P_DECOMPRESSION_ERROR,"Compressed data didn't end when region ended."); + /* + else + return 0; + www.zlib.org + */ + } + + while(length > 0) + { + unsigned len; + + if(&arg->out[arg->offset] == arg->zstream.next_out) + { + int ret; + + arg->zstream.next_out=arg->out; + arg->zstream.avail_out=sizeof arg->out; + arg->offset=0; + if(arg->zstream.avail_in == 0) + { + unsigned n=arg->region->length; + + if(!arg->region->indeterminate) + { + n-=arg->region->length_read; + if(n > sizeof arg->in) + n=sizeof arg->in; + } + else + n=sizeof arg->in; + + if(!ops_stacked_limited_read(arg->in,n,arg->region, + errors,rinfo,cbinfo)) + return -1; + + arg->zstream.next_in=arg->in; + arg->zstream.avail_in=arg->region->indeterminate + ? arg->region->last_read : n; + } + + ret=inflate(&arg->zstream,Z_SYNC_FLUSH); + if(ret == Z_STREAM_END) + { + if(!arg->region->indeterminate + && arg->region->length_read != arg->region->length) + OPS_ERROR(cbinfo->errors,OPS_E_P_DECOMPRESSION_ERROR,"Compressed stream ended before packet end."); + } + else if(ret != Z_OK) + { + fprintf(stderr,"ret=%d\n",ret); + OPS_ERROR(cbinfo->errors,OPS_E_P_DECOMPRESSION_ERROR, arg->zstream.msg); + } + arg->inflate_ret=ret; + } + assert(arg->zstream.next_out > &arg->out[arg->offset]); + len=arg->zstream.next_out-&arg->out[arg->offset]; + if(len > length) + len=length; + memcpy(dest,&arg->out[arg->offset],len); + arg->offset+=len; + length-=len; + } + + return saved; + } + +// \todo remove code duplication between this and zlib_compressed_data_reader +static int bzip2_compressed_data_reader(void *dest,size_t length, + ops_error_t **errors, + ops_reader_info_t *rinfo, + ops_parse_cb_info_t *cbinfo) + { + bz_decompress_arg_t *arg=ops_reader_get_arg(rinfo); + assert(arg->type==OPS_C_BZIP2); + + //ops_parser_content_t content; + int saved=length; + + if(arg->inflate_ret == BZ_STREAM_END + && arg->bzstream.next_out == &arg->out[arg->offset]) + return 0; + + if(arg->region->length_read == arg->region->length) + { + if(arg->inflate_ret != BZ_STREAM_END) + OPS_ERROR(cbinfo->errors, OPS_E_P_DECOMPRESSION_ERROR,"Compressed data didn't end when region ended."); + } + + while(length > 0) + { + unsigned len; + + if(&arg->out[arg->offset] == arg->bzstream.next_out) + { + int ret; + + arg->bzstream.next_out=(char *) arg->out; + arg->bzstream.avail_out=sizeof arg->out; + arg->offset=0; + if(arg->bzstream.avail_in == 0) + { + unsigned n=arg->region->length; + + if(!arg->region->indeterminate) + { + n-=arg->region->length_read; + if(n > sizeof arg->in) + n=sizeof arg->in; + } + else + n=sizeof arg->in; + + if(!ops_stacked_limited_read((unsigned char *)arg->in,n,arg->region, + errors,rinfo,cbinfo)) + return -1; + + arg->bzstream.next_in=arg->in; + arg->bzstream.avail_in=arg->region->indeterminate + ? arg->region->last_read : n; + } + + ret=BZ2_bzDecompress(&arg->bzstream); + if(ret == BZ_STREAM_END) + { + if(!arg->region->indeterminate + && arg->region->length_read != arg->region->length) + OPS_ERROR(cbinfo->errors,OPS_E_P_DECOMPRESSION_ERROR,"Compressed stream ended before packet end."); + } + else if(ret != BZ_OK) + { + OPS_ERROR_1(cbinfo->errors,OPS_E_P_DECOMPRESSION_ERROR,"Invalid return %d from BZ2_bzDecompress", ret); + } + arg->inflate_ret=ret; + } + assert(arg->bzstream.next_out > &arg->out[arg->offset]); + len=arg->bzstream.next_out-&arg->out[arg->offset]; + if(len > length) + len=length; + memcpy(dest,&arg->out[arg->offset],len); + arg->offset+=len; + length-=len; + } + + return saved; + } + +/** + * \ingroup Core_Compress + * + * \param *region Pointer to a region + * \param *parse_info How to parse + * \param type Which compression type to expect +*/ + +int ops_decompress(ops_region_t *region,ops_parse_info_t *parse_info, + ops_compression_type_t type) + { + z_decompress_arg_t z_arg; + bz_decompress_arg_t bz_arg; + int ret; + + switch (type) + { + case OPS_C_ZIP: + case OPS_C_ZLIB: + memset(&z_arg,'\0',sizeof z_arg); + + z_arg.region=region; + z_arg.offset=0; + z_arg.type=type; + + z_arg.zstream.next_in=Z_NULL; + z_arg.zstream.avail_in=0; + z_arg.zstream.next_out=z_arg.out; + z_arg.zstream.zalloc=Z_NULL; + z_arg.zstream.zfree=Z_NULL; + z_arg.zstream.opaque=Z_NULL; + break; + + case OPS_C_BZIP2: + memset(&bz_arg,'\0',sizeof bz_arg); + + bz_arg.region=region; + bz_arg.offset=0; + bz_arg.type=type; + + bz_arg.bzstream.next_in=NULL; + bz_arg.bzstream.avail_in=0; + bz_arg.bzstream.next_out=bz_arg.out; + bz_arg.bzstream.bzalloc=NULL; + bz_arg.bzstream.bzfree=NULL; + bz_arg.bzstream.opaque=NULL; + break; + + default: + OPS_ERROR_1(&parse_info->errors, OPS_E_ALG_UNSUPPORTED_COMPRESS_ALG, "Compression algorithm %d is not yet supported", type); + return 0; + } + + switch(type) + { + case OPS_C_ZIP: + ret=inflateInit2(&z_arg.zstream,-15); + break; + + case OPS_C_ZLIB: + ret=inflateInit(&z_arg.zstream); + break; + + case OPS_C_BZIP2: + /* + OPS_ERROR_1(&parse_info->errors, OPS_E_ALG_UNSUPPORTED_COMPRESS_ALG, "Compression algorithm %s is not yet supported", "BZIP2"); + return 0; + */ + ret=BZ2_bzDecompressInit(&bz_arg.bzstream, 1, 0); + break; + + default: + OPS_ERROR_1(&parse_info->errors, OPS_E_ALG_UNSUPPORTED_COMPRESS_ALG, "Compression algorithm %d is not yet supported", type); + return 0; + } + + switch (type) + { + case OPS_C_ZIP: + case OPS_C_ZLIB: + if(ret != Z_OK) + { + OPS_ERROR_1(&parse_info->errors, OPS_E_P_DECOMPRESSION_ERROR, "Cannot initialise ZIP or ZLIB stream for decompression: error=%d", ret); + return 0; + } + ops_reader_push(parse_info,zlib_compressed_data_reader,NULL,&z_arg); + break; + + case OPS_C_BZIP2: + if (ret != BZ_OK) + { + OPS_ERROR_1(&parse_info->errors, OPS_E_P_DECOMPRESSION_ERROR, "Cannot initialise BZIP2 stream for decompression: error=%d", ret); + return 0; + } + ops_reader_push(parse_info,bzip2_compressed_data_reader,NULL,&bz_arg); + break; + + default: + OPS_ERROR_1(&parse_info->errors, OPS_E_ALG_UNSUPPORTED_COMPRESS_ALG, "Compression algorithm %d is not yet supported", type); + return 0; + } + + ret=ops_parse(parse_info); + + ops_reader_pop(parse_info); + + return ret; + } + +/** +\ingroup Core_WritePackets +\brief Writes Compressed packet +\param data Data to write out +\param len Length of data +\param cinfo Write settings +\return ops_true if OK; else ops_false +*/ + +ops_boolean_t ops_write_compressed(const unsigned char *data, + const unsigned int len, + ops_create_info_t *cinfo) + { + int r=0; + int sz_in=0; + int sz_out=0; + compress_arg_t* compress=ops_mallocz(sizeof *compress); + + // compress the data + const int level=Z_DEFAULT_COMPRESSION; // \todo allow varying levels + compress->stream.zalloc=Z_NULL; + compress->stream.zfree=Z_NULL; + compress->stream.opaque=NULL; + + // all other fields set to zero by use of ops_mallocz + + if (deflateInit(&compress->stream,level) != Z_OK) + { + // can't initialise + assert(0); + } + + // do necessary transformation + // copy input to maintain const'ness of src + assert(compress->src==NULL); + assert(compress->dst==NULL); + + sz_in=len * sizeof (unsigned char); + sz_out= (sz_in * 1.01) + 12; // from zlib webpage + compress->src=ops_mallocz(sz_in); + compress->dst=ops_mallocz(sz_out); + memcpy(compress->src,data,len); + + // setup stream + compress->stream.next_in=compress->src; + compress->stream.avail_in=sz_in; + compress->stream.total_in=0; + + compress->stream.next_out=compress->dst; + compress->stream.avail_out=sz_out; + compress->stream.total_out=0; + + r=deflate(&compress->stream, Z_FINISH); + assert(r==Z_STREAM_END); // need to loop if not + + // write it out + return (ops_write_ptag(OPS_PTAG_CT_COMPRESSED, cinfo) + && ops_write_length(1+compress->stream.total_out, cinfo) + && ops_write_scalar(OPS_C_ZLIB,1,cinfo) + && ops_write(compress->dst, compress->stream.total_out,cinfo)); + } + +// EOF diff --git a/openpgpsdk/src/create.c b/openpgpsdk/src/create.c new file mode 100644 index 000000000..d216a7083 --- /dev/null +++ b/openpgpsdk/src/create.c @@ -0,0 +1,1258 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + */ + +#include + +#include +#include +#include +#include +#include +#include +#include "keyring_local.h" +#include +#include +#include +#include +#include +#include +#ifndef WIN32 +#include +#endif + +#include +#include + +static int debug=0; + +/** + * \ingroup Core_Create + * \param length + * \param type + * \param info + * \return ops_true if OK, otherwise ops_false + */ + +ops_boolean_t ops_write_ss_header(unsigned length,ops_content_tag_t type, + ops_create_info_t *info) + { + return ops_write_length(length,info) + && ops_write_scalar(type-OPS_PTAG_SIGNATURE_SUBPACKET_BASE,1,info); + } + +/* XXX: the general idea of _fast_ is that it doesn't copy stuff + * the safe (i.e. non _fast_) version will, and so will also need to + * be freed. */ + +/** + * \ingroup Core_Create + * + * ops_fast_create_user_id() sets id->user_id to the given user_id. + * This is fast because it is only copying a char*. However, if user_id + * is changed or freed in the future, this could have injurious results. + * \param id + * \param user_id + */ + +void ops_fast_create_user_id(ops_user_id_t *id,unsigned char *user_id) + { + id->user_id=user_id; + } + +/** + * \ingroup Core_WritePackets + * \brief Writes a User Id packet + * \param id + * \param info + * \return ops_true if OK, otherwise ops_false + */ +ops_boolean_t ops_write_struct_user_id(ops_user_id_t *id, + ops_create_info_t *info) + { + return ops_write_ptag(OPS_PTAG_CT_USER_ID,info) + && ops_write_length(strlen((char *)id->user_id),info) + && ops_write(id->user_id,strlen((char *)id->user_id),info); + } + +/** + * \ingroup Core_WritePackets + * \brief Write a User Id packet. + * \param user_id + * \param info + * + * \return return value from ops_write_struct_user_id() + */ +ops_boolean_t ops_write_user_id(const unsigned char *user_id,ops_create_info_t *info) + { + ops_user_id_t id; + + id.user_id=(unsigned char *)user_id; + return ops_write_struct_user_id(&id,info); + } + +/** +\ingroup Core_MPI +*/ +static unsigned mpi_length(const BIGNUM *bn) + { + return 2+(BN_num_bits(bn)+7)/8; + } + +static unsigned public_key_length(const ops_public_key_t *key) + { + switch(key->algorithm) + { + case OPS_PKA_RSA: + return mpi_length(key->key.rsa.n)+mpi_length(key->key.rsa.e); + + default: + assert(!"unknown key algorithm"); + } + /* not reached */ + return 0; + } + +static unsigned secret_key_length(const ops_secret_key_t *key) + { + int l; + + switch(key->public_key.algorithm) + { + case OPS_PKA_RSA: + l=mpi_length(key->key.rsa.d)+mpi_length(key->key.rsa.p) + +mpi_length(key->key.rsa.q)+mpi_length(key->key.rsa.u); + break; + + default: + assert(!"unknown key algorithm"); + } + + return l+public_key_length(&key->public_key); + } + +/** + * \ingroup Core_Create + * \param key + * \param time + * \param n + * \param e +*/ +void ops_fast_create_rsa_public_key(ops_public_key_t *key,time_t time, + BIGNUM *n,BIGNUM *e) + { + key->version=4; + key->creation_time=time; + key->algorithm=OPS_PKA_RSA; + key->key.rsa.n=n; + key->key.rsa.e=e; + } + +/* Note that we support v3 keys here because they're needed for + * for verification - the writer doesn't allow them, though */ +static ops_boolean_t write_public_key_body(const ops_public_key_t *key, + ops_create_info_t *info) + { + if(!(ops_write_scalar(key->version,1,info) + && ops_write_scalar(key->creation_time,4,info))) + return ops_false; + + if(key->version != 4 && !ops_write_scalar(key->days_valid,2,info)) + return ops_false; + + if(!ops_write_scalar(key->algorithm,1,info)) + return ops_false; + + switch(key->algorithm) + { + case OPS_PKA_DSA: + return ops_write_mpi(key->key.dsa.p,info) + && ops_write_mpi(key->key.dsa.q,info) + && ops_write_mpi(key->key.dsa.g,info) + && ops_write_mpi(key->key.dsa.y,info); + + case OPS_PKA_RSA: + case OPS_PKA_RSA_ENCRYPT_ONLY: + case OPS_PKA_RSA_SIGN_ONLY: + return ops_write_mpi(key->key.rsa.n,info) + && ops_write_mpi(key->key.rsa.e,info); + + case OPS_PKA_ELGAMAL: + return ops_write_mpi(key->key.elgamal.p,info) + && ops_write_mpi(key->key.elgamal.g,info) + && ops_write_mpi(key->key.elgamal.y,info); + + default: + assert(0); + break; + } + + /* not reached */ + return ops_false; + } + +/* Note that we support v3 keys here because they're needed for + * for verification - the writer doesn't allow them, though */ +static ops_boolean_t write_secret_key_body(const ops_secret_key_t *key, + const unsigned char* passphrase, + const size_t pplen, + ops_create_info_t *info) + { + /* RFC4880 Section 5.5.3 Secret-Key Packet Formats */ + + ops_crypt_t crypt; + ops_hash_t hash; + unsigned char hashed[OPS_SHA1_HASH_SIZE]; + unsigned char session_key[CAST_KEY_LENGTH]; + unsigned int done=0; + unsigned int i=0; + + if(!write_public_key_body(&key->public_key,info)) + return ops_false; + + assert(key->s2k_usage==OPS_S2KU_ENCRYPTED_AND_HASHED); /* = 254 */ + if(!ops_write_scalar(key->s2k_usage,1,info)) + return ops_false; + + assert(key->algorithm==OPS_SA_CAST5); + if (!ops_write_scalar(key->algorithm,1,info)) + return ops_false; + + assert(key->s2k_specifier==OPS_S2KS_SIMPLE || key->s2k_specifier==OPS_S2KS_SALTED); // = 1 \todo could also be iterated-and-salted + if (!ops_write_scalar(key->s2k_specifier,1,info)) + return ops_false; + + assert(key->hash_algorithm==OPS_HASH_SHA1); + if (!ops_write_scalar(key->hash_algorithm,1,info)) + return ops_false; + + switch(key->s2k_specifier) + { + case OPS_S2KS_SIMPLE: + // nothing more to do + break; + + case OPS_S2KS_SALTED: + // 8-octet salt value + ops_random((void *)&key->salt[0],OPS_SALT_SIZE); + if (!ops_write(key->salt, OPS_SALT_SIZE, info)) + return ops_false; + break; + + /* \todo + case OPS_S2KS_ITERATED_AND_SALTED: + // 8-octet salt value + // 1-octet count + break; + */ + + default: + fprintf(stderr,"invalid/unsupported s2k specifier %d\n", key->s2k_specifier); + assert(0); + } + + if (!ops_write(&key->iv[0],ops_block_size(key->algorithm),info)) + return ops_false; + + /* create the session key for encrypting the algorithm-specific fields */ + + switch(key->s2k_specifier) + { + case OPS_S2KS_SIMPLE: + case OPS_S2KS_SALTED: + // RFC4880: section 3.7.1.1 and 3.7.1.2 + + done=0; + for (i=0; donehash_algorithm); + hash.init(&hash); + + // preload if iterating + for (j=0; js2k_specifier==OPS_S2KS_SALTED) + { hash.add(&hash, key->salt, OPS_SALT_SIZE); } + + hash.add(&hash, passphrase, pplen); + hash.finish(&hash, hashed); + + // if more in hash than is needed by session key, use the leftmost octets + memcpy(session_key+(i*SHA_DIGEST_LENGTH), hashed, use); + done += use; + assert(done<=CAST_KEY_LENGTH); + } + + break; + + /* \todo + case OPS_S2KS_ITERATED_AND_SALTED: + // 8-octet salt value + // 1-octet count + break; + */ + + default: + fprintf(stderr,"invalid/unsupported s2k specifier %d\n", key->s2k_specifier); + assert(0); + } + + /* use this session key to encrypt */ + + ops_crypt_any(&crypt,key->algorithm); + crypt.set_iv(&crypt, key->iv); + crypt.set_key(&crypt, session_key); + ops_encrypt_init(&crypt); + + if (debug) + { + unsigned int i=0; + fprintf(stderr,"\nWRITING:\niv="); + for (i=0; ialgorithm); i++) + { + fprintf(stderr, "%02x ", key->iv[i]); + } + fprintf(stderr,"\n"); + + fprintf(stderr,"key="); + for (i=0; ipublic_key.algorithm) + { + // case OPS_PKA_DSA: + // return ops_write_mpi(key->key.dsa.x,info); + + case OPS_PKA_RSA: + case OPS_PKA_RSA_ENCRYPT_ONLY: + case OPS_PKA_RSA_SIGN_ONLY: + + if(!ops_write_mpi(key->key.rsa.d,info) + || !ops_write_mpi(key->key.rsa.p,info) + || !ops_write_mpi(key->key.rsa.q,info) + || !ops_write_mpi(key->key.rsa.u,info)) + { + if (debug) + { fprintf(stderr,"4 x mpi not written - problem\n"); } + return ops_false; + } + + break; + + // case OPS_PKA_ELGAMAL: + // return ops_write_mpi(key->key.elgamal.x,info); + + default: + assert(0); + break; + } + + if(!ops_write(key->checkhash, OPS_CHECKHASH_SIZE, info)) + return ops_false; + + ops_writer_pop(info); + + return ops_true; + } + + +/** + \ingroup HighLevel_KeyWrite + + \brief Writes a transferable PGP public key to the given output stream. + + \param keydata Key to be written + \param armoured Flag is set for armoured output + \param info Output stream + + Example code: + \code + void example(const ops_keydata_t* keydata) + { + ops_boolean_t armoured=ops_true; + char* filename="/tmp/testkey.asc"; + + int fd; + ops_boolean_t overwrite=ops_true; + ops_create_info_t* cinfo; + + fd=ops_setup_file_write(&cinfo, filename, overwrite); + ops_write_transferable_public_key(keydata,armoured,cinfo); + ops_teardown_file_write(cinfo,fd); + } + \endcode +*/ + +ops_boolean_t ops_write_transferable_public_key(const ops_keydata_t *keydata, ops_boolean_t armoured, ops_create_info_t *info) + { + ops_boolean_t rtn; + unsigned int i=0,j=0; + + if (armoured) + { ops_writer_push_armoured(info, OPS_PGP_PUBLIC_KEY_BLOCK); } + + // public key + rtn=ops_write_struct_public_key(&keydata->key.skey.public_key,info); + if (rtn!=ops_true) + return rtn; + + // TODO: revocation signatures go here + + // user ids and corresponding signatures + for (i=0; inuids; i++) + { + ops_user_id_t* uid=&keydata->uids[i]; + + rtn=ops_write_struct_user_id(uid, info); + + if (!rtn) + return rtn; + + // find signature for this packet if it exists + for (j=0; jnsigs; j++) + { + sigpacket_t* sig=&keydata->sigs[i]; + if (!strcmp((char *)sig->userid->user_id, (char *)uid->user_id)) + { + rtn=ops_write(sig->packet->raw, sig->packet->length, info); + if (!rtn) + return !rtn; + } + } + } + + // TODO: user attributes and corresponding signatures + + // subkey packets and corresponding signatures and optional revocation + + if (armoured) + { + writer_info_finalise(&info->errors, &info->winfo); + ops_writer_pop(info); + } + + return rtn; + } + +/** + \ingroup HighLevel_KeyWrite + + \brief Writes a transferable PGP secret key to the given output stream. + + \param keydata Key to be written + \param passphrase + \param pplen + \param armoured Flag is set for armoured output + \param info Output stream + + Example code: + \code + void example(const ops_keydata_t* keydata) + { + const unsigned char* passphrase=NULL; + const size_t passphraselen=0; + ops_boolean_t armoured=ops_true; + + int fd; + char* filename="/tmp/testkey.asc"; + ops_boolean_t overwrite=ops_true; + ops_create_info_t* cinfo; + + fd=ops_setup_file_write(&cinfo, filename, overwrite); + ops_write_transferable_secret_key(keydata,passphrase,pplen,armoured,cinfo); + ops_teardown_file_write(cinfo,fd); + } + \endcode +*/ + +ops_boolean_t ops_write_transferable_secret_key(const ops_keydata_t *keydata, const unsigned char* passphrase, const size_t pplen, ops_boolean_t armoured, ops_create_info_t *info) + { + ops_boolean_t rtn; + unsigned int i=0,j=0; + + if (armoured) + { ops_writer_push_armoured(info,OPS_PGP_PRIVATE_KEY_BLOCK); } + + // public key + rtn=ops_write_struct_secret_key(&keydata->key.skey,passphrase,pplen,info); + if (rtn!=ops_true) + return rtn; + + // TODO: revocation signatures go here + + // user ids and corresponding signatures + for (i=0; inuids; i++) + { + ops_user_id_t* uid=&keydata->uids[i]; + + rtn=ops_write_struct_user_id(uid, info); + + if (!rtn) + return rtn; + + // find signature for this packet if it exists + for (j=0; jnsigs; j++) + { + sigpacket_t* sig=&keydata->sigs[i]; + if (!strcmp((char *)sig->userid->user_id, (char *)uid->user_id)) + { + rtn=ops_write(sig->packet->raw, sig->packet->length, info); + if (!rtn) + return !rtn; + } + } + } + + // TODO: user attributes and corresponding signatures + + // subkey packets and corresponding signatures and optional revocation + + if (armoured) + { + writer_info_finalise(&info->errors, &info->winfo); + ops_writer_pop(info); + } + + return rtn; + } + +/** + * \ingroup Core_WritePackets + * \brief Writes a Public Key packet + * \param key + * \param info + * \return ops_true if OK, otherwise ops_false + */ +ops_boolean_t ops_write_struct_public_key(const ops_public_key_t *key, + ops_create_info_t *info) + { + assert(key->version == 4); + + return ops_write_ptag(OPS_PTAG_CT_PUBLIC_KEY,info) + && ops_write_length(1+4+1+public_key_length(key),info) + && write_public_key_body(key,info); + } + +/** + * \ingroup Core_WritePackets + * \brief Writes one RSA public key packet. + * \param time Creation time + * \param n RSA public modulus + * \param e RSA public encryption exponent + * \param info Writer settings + * + * \return ops_true if OK, otherwise ops_false + */ + +ops_boolean_t ops_write_rsa_public_key(time_t time,const BIGNUM *n, + const BIGNUM *e, + ops_create_info_t *info) + { + ops_public_key_t key; + + ops_fast_create_rsa_public_key(&key,time,DECONST(BIGNUM,n), + DECONST(BIGNUM,e)); + return ops_write_struct_public_key(&key,info); + } + +/** + * \ingroup Core_Create + * \param out + * \param key + * \param make_packet + */ + +void ops_build_public_key(ops_memory_t *out,const ops_public_key_t *key, + ops_boolean_t make_packet) + { + ops_create_info_t *info; + + info=ops_create_info_new(); + + ops_memory_init(out,128); + ops_writer_set_memory(info,out); + + write_public_key_body(key,info); + + if(make_packet) + ops_memory_make_packet(out,OPS_PTAG_CT_PUBLIC_KEY); + + ops_create_info_delete(info); + } + +/** + * \ingroup Core_Create + * + * Create an RSA secret key structure. If a parameter is marked as + * [OPTIONAL], then it can be omitted and will be calculated from + * other parameters - or, in the case of e, will default to 0x10001. + * + * Parameters are _not_ copied, so will be freed if the structure is + * freed. + * + * \param key The key structure to be initialised. + * \param time + * \param d The RSA parameter d (=e^-1 mod (p-1)(q-1)) [OPTIONAL] + * \param p The RSA parameter p + * \param q The RSA parameter q (q > p) + * \param u The RSA parameter u (=p^-1 mod q) [OPTIONAL] + * \param n The RSA public parameter n (=p*q) [OPTIONAL] + * \param e The RSA public parameter e */ + +void ops_fast_create_rsa_secret_key(ops_secret_key_t *key,time_t time, + BIGNUM *d,BIGNUM *p,BIGNUM *q,BIGNUM *u, + BIGNUM *n,BIGNUM *e) + { + ops_fast_create_rsa_public_key(&key->public_key,time,n,e); + + // XXX: calculate optionals + key->key.rsa.d=d; + key->key.rsa.p=p; + key->key.rsa.q=q; + key->key.rsa.u=u; + + key->s2k_usage=OPS_S2KU_NONE; + + // XXX: sanity check and add errors... + } + +/** + * \ingroup Core_WritePackets + * \brief Writes a Secret Key packet. + * \param key The secret key + * \param passphrase The passphrase + * \param pplen Length of passphrase + * \param info + * \return ops_true if OK; else ops_false + */ +ops_boolean_t ops_write_struct_secret_key(const ops_secret_key_t *key, + const unsigned char* passphrase, + const size_t pplen, + ops_create_info_t *info) + { + int length=0; + + assert(key->public_key.version == 4); + + // Ref: RFC4880 Section 5.5.3 + + // public_key, excluding MPIs + length += 1+4+1+1; + + // s2k usage + length+=1; + + switch (key->s2k_usage) + { + case OPS_S2KU_NONE: + // nothing to add + break; + + case OPS_S2KU_ENCRYPTED_AND_HASHED: // 254 + case OPS_S2KU_ENCRYPTED: // 255 + + // Ref: RFC4880 Section 3.7 + length+=1; // s2k_specifier + + switch(key->s2k_specifier) + { + case OPS_S2KS_SIMPLE: + length+=1; // hash algorithm + break; + + case OPS_S2KS_SALTED: + length+=1+8; // hash algorithm + salt + break; + + case OPS_S2KS_ITERATED_AND_SALTED: + length+=1+8+1; // hash algorithm, salt + count + break; + + default: + assert(0); + } + break; + + default: + assert(0); + } + + // IV + if (key->s2k_usage != 0) + { + length += ops_block_size(key->algorithm); + } + + // checksum or hash + switch (key->s2k_usage) + { + case 0: + case 255: + length += 2; + break; + + case 254: + length += 20; + break; + + default: + assert(0); + } + + // secret key and public key MPIs + length += secret_key_length(key); + + return ops_write_ptag(OPS_PTAG_CT_SECRET_KEY,info) + // && ops_write_length(1+4+1+1+secret_key_length(key)+2,info) + && ops_write_length(length,info) + && write_secret_key_body(key,passphrase,pplen,info); + } + +/** + * \ingroup Core_Create + * + * \brief Create a new ops_create_info_t structure. + * + * \return the new structure. + * \note It is the responsiblity of the caller to call ops_create_info_delete(). + * \sa ops_create_info_delete() + */ +ops_create_info_t *ops_create_info_new(void) + { return ops_mallocz(sizeof(ops_create_info_t)); } + +/** + * \ingroup Core_Create + * \brief Delete an ops_create_info_t strucut and associated resources. + * + * Delete an ops_create_info_t structure. If a writer is active, then + * that is also deleted. + * + * \param info the structure to be deleted. + */ +void ops_create_info_delete(ops_create_info_t *info) + { + writer_info_delete(&info->winfo); + free(info); + } + +/** + \ingroup Core_Create + \brief Calculate the checksum for a session key + \param session_key Session Key to use + \param cs Checksum to be written + \return ops_true if OK; else ops_false +*/ +ops_boolean_t ops_calc_session_key_checksum(ops_pk_session_key_t *session_key, unsigned char cs[2]) + { + unsigned int i=0; + unsigned long checksum=0; + + if (!ops_is_sa_supported(session_key->symmetric_algorithm)) + return ops_false; + + for (i=0; isymmetric_algorithm); i++) + { + checksum+=session_key->key[i]; + } + checksum = checksum % 65536; + + cs[0]=checksum >> 8; + cs[1]=checksum & 0xFF; + + return ops_true; + // fprintf(stderr,"\nm buf checksum: "); + // fprintf(stderr," %2x",cs[0]); + // fprintf(stderr," %2x\n",cs[1]); + } + +static ops_boolean_t create_unencoded_m_buf(ops_pk_session_key_t *session_key, unsigned char *m_buf) + { + int i=0; + // unsigned long checksum=0; + + // m_buf is the buffer which will be encoded in PKCS#1 block + // encoding to form the "m" value used in the + // Public Key Encrypted Session Key Packet + // as defined in RFC Section 5.1 "Public-Key Encrypted Session Key Packet" + + m_buf[0]=session_key->symmetric_algorithm; + + assert(session_key->symmetric_algorithm==OPS_SA_CAST5); + for (i=0; ikey[i]; + } + + return(ops_calc_session_key_checksum(session_key, m_buf+1+CAST_KEY_LENGTH)); + } + +/** +\ingroup Core_Create +\brief implementation of EME-PKCS1-v1_5-ENCODE, as defined in OpenPGP RFC +\param M +\param mLen +\param pkey +\param EM +\return ops_true if OK; else ops_false +*/ +ops_boolean_t encode_m_buf(const unsigned char *M, size_t mLen, + const ops_public_key_t *pkey, + unsigned char* EM +) + { + unsigned int k; + unsigned i; + + // implementation of EME-PKCS1-v1_5-ENCODE, as defined in OpenPGP RFC + + assert(pkey->algorithm == OPS_PKA_RSA); + + k=BN_num_bytes(pkey->key.rsa.n); + assert(mLen <= k-11); + if (mLen > k-11) + { + fprintf(stderr,"message too long\n"); + return ops_false; + } + + // these two bytes defined by RFC + EM[0]=0x00; + EM[1]=0x02; + + // add non-zero random bytes of length k - mLen -3 + for(i=2 ; i < k-mLen-1 ; ++i) + do + ops_random(EM+i, 1); + while(EM[i] == 0); + + assert (i >= 8+2); + + EM[i++]=0; + + memcpy(EM+i, M, mLen); + + + if (debug) + { + unsigned int i=0; + fprintf(stderr,"Encoded Message: \n"); + for (i=0; ikey.rsa.n); + unsigned char* encoded_m_buf = ops_mallocz(sz_encoded_m_buf); + + ops_pk_session_key_t *session_key=ops_mallocz(sizeof *session_key); + + assert(key->type == OPS_PTAG_CT_PUBLIC_KEY); + session_key->version=OPS_PKSK_V3; + memcpy(session_key->key_id, key->key_id, sizeof session_key->key_id); + + if (debug) + { + unsigned int i=0; + fprintf(stderr,"Encrypting for RSA key id : "); + for (i=0; ikey_id; i++) + fprintf(stderr,"%2x ", key->key_id[i]); + fprintf(stderr,"\n"); + } + + assert(key->key.pkey.algorithm == OPS_PKA_RSA); + session_key->algorithm=key->key.pkey.algorithm; + + // \todo allow user to specify other algorithm + session_key->symmetric_algorithm=OPS_SA_CAST5; + ops_random(session_key->key, CAST_KEY_LENGTH); + + if (debug) + { + unsigned int i=0; + fprintf(stderr,"CAST5 session key created (len=%d):\n ", CAST_KEY_LENGTH); + for (i=0; ikey[i]); + fprintf(stderr,"\n"); + } + + if (create_unencoded_m_buf(session_key, &unencoded_m_buf[0])==ops_false) + { + free(encoded_m_buf); + return NULL; + } + + if (debug) + { + unsigned int i=0; + printf("unencoded m buf:\n"); + for (i=0; iparameters)) + { + free (encoded_m_buf); + return NULL; + } + + free(encoded_m_buf); + return session_key; + } + +/** +\ingroup Core_WritePackets +\brief Writes Public Key Session Key packet +\param info Write settings +\param pksk Public Key Session Key to write out +\return ops_true if OK; else ops_false +*/ +ops_boolean_t ops_write_pk_session_key(ops_create_info_t *info, + ops_pk_session_key_t *pksk) + { + assert(pksk); + assert(pksk->algorithm == OPS_PKA_RSA); + + return ops_write_ptag(OPS_PTAG_CT_PK_SESSION_KEY, info) + && ops_write_length(1 + 8 + 1 + BN_num_bytes(pksk->parameters.rsa.encrypted_m) + 2, info) + && ops_write_scalar(pksk->version, 1, info) + && ops_write(pksk->key_id, 8, info) + && ops_write_scalar(pksk->algorithm, 1, info) + && ops_write_mpi(pksk->parameters.rsa.encrypted_m, info) + //?? && ops_write_scalar(0, 2, info); + ; + } + +/** +\ingroup Core_WritePackets +\brief Writes MDC packet +\param hashed Hash for MDC +\param info Write settings +\return ops_true if OK; else ops_false +*/ + +ops_boolean_t ops_write_mdc(const unsigned char *hashed, + ops_create_info_t* info) + { + // write it out + return ops_write_ptag(OPS_PTAG_CT_MDC, info) + && ops_write_length(OPS_SHA1_HASH_SIZE,info) + && ops_write(hashed, OPS_SHA1_HASH_SIZE, info); + } + +/** +\ingroup Core_WritePackets +\brief Writes Literal Data packet from buffer +\param data Buffer to write out +\param maxlen Max length of buffer +\param type Literal Data Type +\param info Write settings +\return ops_true if OK; else ops_false +*/ +ops_boolean_t ops_write_literal_data_from_buf(const unsigned char *data, + const int maxlen, + const ops_literal_data_type_t type, + ops_create_info_t *info) + { + /* + * RFC4880 does not specify a meaning for filename or date. + * It is implementation-dependent. + * We will not implement them. + */ + // \todo do we need to check text data for line endings ? + return ops_write_ptag(OPS_PTAG_CT_LITERAL_DATA, info) + && ops_write_length(1+1+4+maxlen,info) + && ops_write_scalar(type, 1, info) + && ops_write_scalar(0, 1, info) + && ops_write_scalar(0, 4, info) + && ops_write(data, maxlen, info); + } + +/** +\ingroup Core_WritePackets +\brief Writes Literal Data packet from contents of file +\param filename Name of file to read from +\param type Literal Data Type +\param info Write settings +\return ops_true if OK; else ops_false +*/ + +ops_boolean_t ops_write_literal_data_from_file(const char *filename, + const ops_literal_data_type_t type, + ops_create_info_t *info) + { + size_t initial_size=1024; + int fd=0; + ops_boolean_t rtn; + unsigned char buf[1024]; + ops_memory_t* mem=NULL; + size_t len=0; + +#ifdef WIN32 + fd=open(filename,O_RDONLY | O_BINARY); +#else + fd=open(filename,O_RDONLY); +#endif + if (fd < 0) + return ops_false; + + mem=ops_memory_new(); + ops_memory_init(mem,initial_size); + for (;;) + { + ssize_t n=0; + n=read(fd,buf,1024); + if (!n) + break; + ops_memory_add(mem, &buf[0], n); + } + close(fd); + + // \todo do we need to check text data for line endings ? + len=ops_memory_get_length(mem); + rtn=ops_write_ptag(OPS_PTAG_CT_LITERAL_DATA, info) + && ops_write_length(1+1+4+len,info) + && ops_write_scalar(type, 1, info) + && ops_write_scalar(0, 1, info) // filename + && ops_write_scalar(0, 4, info) // date + && ops_write(ops_memory_get_data(mem), len, info); + + ops_memory_free(mem); + return rtn; + } + +/** + \ingroup HighLevel_General + + \brief Reads contents of file into new ops_memory_t struct. + + \param filename Filename to read from + \param errnum Pointer to error + \return new ops_memory_t pointer containing the contents of the file + + \note If there was an error opening the file or reading from it, errnum is set to the cause + + \note It is the caller's responsibility to call ops_memory_free(mem) +*/ + +ops_memory_t* ops_write_mem_from_file(const char *filename, int* errnum) + { + size_t initial_size=1024; + int fd=0; + unsigned char buf[1024]; + ops_memory_t* mem=NULL; + + *errnum=0; + +#ifdef WIN32 + fd=open(filename,O_RDONLY | O_BINARY); +#else + fd=open(filename,O_RDONLY); +#endif + if (fd < 0) + { + *errnum=errno; + return ops_false; + } + + mem=ops_memory_new(); + ops_memory_init(mem,initial_size); + for (;;) + { + ssize_t n=0; + n=read(fd,buf,1024); + if (n<0) + { + *errnum=errno; + break; + } + if (!n) + break; + ops_memory_add(mem, &buf[0], n); + } + close(fd); + return mem; + } + +/** + \ingroup HighLevel_General + + \brief Reads contents of buffer into file + + \param filename Filename to write to + \param buf Buffer to write to file + \param len Size of buffer + \param overwrite Flag to set whether to overwrite an existing file + \return 1 if OK; 0 if error +*/ + +int ops_write_file_from_buf(const char *filename, const char* buf, const size_t len, const ops_boolean_t overwrite) + { + int fd=0; + size_t n=0; + int flags=0; + + flags=O_WRONLY | O_CREAT; + if (overwrite==ops_true) + flags |= O_TRUNC; + else + flags |= O_EXCL; +#ifdef WIN32 + flags |= O_BINARY; +#endif + fd=open(filename,flags, 0600); + if (fd < 0) + { + perror(NULL); + return 0; + } + + n=write(fd,buf,len); + if (n!=len) + return 0; + + if(!close(fd)) + return 1; + + return 0; + } + +/** +\ingroup Core_WritePackets +\brief Write Symmetrically Encrypted packet +\param data Data to encrypt +\param len Length of data +\param info Write settings +\return ops_true if OK; else ops_false +\note Hard-coded to use AES256 +*/ +ops_boolean_t ops_write_symmetrically_encrypted_data(const unsigned char *data, + const int len, + ops_create_info_t *info) + { + int done=0; + ops_crypt_t crypt_info; + int encrypted_sz=0;// size of encrypted data + unsigned char *encrypted=(unsigned char *)NULL; // buffer to write encrypted data to + + // \todo assume AES256 for now + ops_crypt_any(&crypt_info, OPS_SA_AES_256); + ops_encrypt_init(&crypt_info); + + encrypted_sz=len+crypt_info.blocksize+2; + encrypted=ops_mallocz(encrypted_sz); + + done=ops_encrypt_se(&crypt_info, encrypted, data, len); + assert(done==len); + // printf("len=%d, done: %d\n", len, done); + + return ops_write_ptag(OPS_PTAG_CT_SE_DATA, info) + && ops_write_length(1+encrypted_sz,info) + && ops_write(data, len, info); + } + +/** +\ingroup Core_WritePackets +\brief Write a One Pass Signature packet +\param skey Secret Key to use +\param hash_alg Hash Algorithm to use +\param sig_type Signature type +\param info Write settings +\return ops_true if OK; else ops_false +*/ +ops_boolean_t ops_write_one_pass_sig(const ops_secret_key_t* skey, + const ops_hash_algorithm_t hash_alg, + const ops_sig_type_t sig_type, + ops_create_info_t* info) + { + unsigned char keyid[OPS_KEY_ID_SIZE]; + if (debug) + { fprintf(stderr,"calling ops_keyid in write_one_pass_sig: this calls sha1_init\n"); } + ops_keyid(keyid,&skey->public_key); + + return ops_write_ptag(OPS_PTAG_CT_ONE_PASS_SIGNATURE, info) + && ops_write_length(1+1+1+1+8+1, info) + && ops_write_scalar (3, 1, info) // version + && ops_write_scalar (sig_type, 1, info) + && ops_write_scalar (hash_alg, 1, info) + && ops_write_scalar (skey->public_key.algorithm, 1, info) + && ops_write(keyid, 8, info) + && ops_write_scalar (1, 1, info); + } + +// EOF diff --git a/openpgpsdk/src/crypto.c b/openpgpsdk/src/crypto.c new file mode 100644 index 000000000..3c6a88cad --- /dev/null +++ b/openpgpsdk/src/crypto.c @@ -0,0 +1,400 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include "parse_local.h" + +#include +#include +#include + +#include + +/** +\ingroup Core_MPI +\brief Decrypt and unencode MPI +\param buf Buffer in which to write decrypted unencoded MPI +\param buflen Length of buffer +\param encmpi +\param skey +\return length of MPI +\note only RSA at present +*/ +int ops_decrypt_and_unencode_mpi(unsigned char *buf,unsigned buflen,const BIGNUM *encmpi, + const ops_secret_key_t *skey) + { + unsigned char encmpibuf[8192]; + unsigned char mpibuf[8192]; + unsigned mpisize; + int n; + int i; + + mpisize=BN_num_bytes(encmpi); + /* MPI can't be more than 65,536 */ + assert(mpisize <= sizeof encmpibuf); + BN_bn2bin(encmpi,encmpibuf); + + assert(skey->public_key.algorithm == OPS_PKA_RSA); + + /* + fprintf(stderr,"\nDECRYPTING\n"); + fprintf(stderr,"encrypted data : "); + for (i=0; i<16; i++) + fprintf(stderr,"%2x ", encmpibuf[i]); + fprintf(stderr,"\n"); + */ + + n=ops_rsa_private_decrypt(mpibuf,encmpibuf,(BN_num_bits(encmpi)+7)/8, + &skey->key.rsa,&skey->public_key.key.rsa); + assert(n!=-1); + + /* + fprintf(stderr,"decrypted encoded m buf : "); + for (i=0; i<16; i++) + fprintf(stderr,"%2x ", mpibuf[i]); + fprintf(stderr,"\n"); + */ + + if(n <= 0) + return -1; + + /* + printf(" decrypted=%d ",n); + hexdump(mpibuf,n); + printf("\n"); + */ + + // Decode EME-PKCS1_V1_5 (RFC 2437). + + if(mpibuf[0] != 0 || mpibuf[1] != 2) + return ops_false; + + // Skip the random bytes. + for(i=2 ; i < n && mpibuf[i] ; ++i) + ; + + if(i == n || i < 10) + return ops_false; + + // Skip the zero + ++i; + + // this is the unencoded m buf + if((unsigned)(n-i) <= buflen) + memcpy(buf,mpibuf+i,n-i); + + /* + printf("decoded m buf:\n"); + int j; + for (j=0; jkey.rsa.n)); + + unsigned char encmpibuf[8192]; + int n=0; + + n=ops_rsa_public_encrypt(encmpibuf, encoded_m_buf, sz_encoded_m_buf, &pkey->key.rsa); + assert(n!=-1); + + if(n <= 0) + return ops_false; + + skp->rsa.encrypted_m=BN_bin2bn(encmpibuf, n, NULL); + + /* + fprintf(stderr,"encrypted mpi buf : "); + int i; + for (i=0; i<16; i++) + fprintf(stderr,"%2x ", encmpibuf[i]); + fprintf(stderr,"\n"); + */ + + return ops_true; + } + +#define MAXBUF 1024 + +static ops_parse_cb_return_t +callback_write_parsed(const ops_parser_content_t *content_,ops_parse_cb_info_t *cbinfo); + +/** +\ingroup HighLevel_Crypto +Encrypt a file +\param input_filename Name of file to be encrypted +\param output_filename Name of file to write to. If NULL, name is constructed from input_filename +\param pub_key Public Key to encrypt file for +\param use_armour Write armoured text, if set +\param allow_overwrite Allow output file to be overwrwritten if it exists +\return ops_true if OK; else ops_false +*/ +ops_boolean_t ops_encrypt_file(const char* input_filename, const char* output_filename, const ops_keydata_t *pub_key, const ops_boolean_t use_armour, const ops_boolean_t allow_overwrite) + { + int fd_in=0; + int fd_out=0; + + ops_create_info_t *cinfo; + +#ifdef WIN32 + fd_in=open(input_filename,O_RDONLY | O_BINARY); +#else + fd_in=open(input_filename,O_RDONLY); +#endif + if(fd_in < 0) + { + perror(input_filename); + return ops_false; + } + + fd_out=ops_setup_file_write(&cinfo, output_filename, allow_overwrite); + if (fd_out < 0) + return ops_false; + + // set armoured/not armoured here + if (use_armour) + ops_writer_push_armoured_message(cinfo); + + // Push the encrypted writer + ops_writer_push_encrypt_se_ip(cinfo,pub_key); + + // Do the writing + + unsigned char* buf=NULL; + size_t bufsz=16; + int done=0; + for (;;) + { + buf=realloc(buf,done+bufsz); + + int n=0; + + n=read(fd_in,buf+done,bufsz); + if (!n) + break; + assert(n>=0); + done+=n; + } + + // This does the writing + ops_write(buf,done,cinfo); + + // tidy up + close(fd_in); + free(buf); + ops_teardown_file_write(cinfo,fd_out); + + return ops_true; + } + +/** + \ingroup HighLevel_Crypto + \brief Decrypt a file. + \param input_filename Name of file to be decrypted + \param output_filename Name of file to write to. If NULL, the filename is constructed from the input filename, following GPG conventions. + \param keyring Keyring to use + \param use_armour Expect armoured text, if set + \param allow_overwrite Allow output file to overwritten, if set. + \param cb_get_passphrase Callback to use to get passphrase +*/ + +ops_boolean_t ops_decrypt_file(const char* input_filename, const char* output_filename, ops_keyring_t* keyring, const ops_boolean_t use_armour, const ops_boolean_t allow_overwrite, ops_parse_cb_t* cb_get_passphrase) + { + int fd_in=0; + int fd_out=0; + char* myfilename=NULL; + + // + ops_parse_info_t *pinfo=NULL; + + // setup for reading from given input file + fd_in=ops_setup_file_read(&pinfo, input_filename, + NULL, + callback_write_parsed, + ops_false); + if (fd_in < 0) + { + perror(input_filename); + return ops_false; + } + + // setup output filename + + if (output_filename) + { + fd_out=ops_setup_file_write(&pinfo->cbinfo.cinfo, output_filename, allow_overwrite); + + if (fd_out < 0) + { + perror(output_filename); + ops_teardown_file_read(pinfo,fd_in); + return ops_false; + } + } + else + { + int suffixlen=4; + char *defaultsuffix=".decrypted"; + const char *suffix=input_filename+strlen((char *)input_filename)-suffixlen; + if (!strcmp(suffix,".gpg") || !strcmp(suffix,".asc")) + { + myfilename=ops_mallocz(strlen(input_filename)-suffixlen+1); + strncpy(myfilename,input_filename,strlen(input_filename)-suffixlen); + } + else + { + unsigned filenamelen=strlen(input_filename)+strlen(defaultsuffix)+1; + myfilename=ops_mallocz(filenamelen); + snprintf(myfilename,filenamelen,"%s%s",input_filename,defaultsuffix); + } + + fd_out=ops_setup_file_write(&pinfo->cbinfo.cinfo, myfilename, allow_overwrite); + + if (fd_out < 0) + { + perror(myfilename); + free(myfilename); + ops_teardown_file_read(pinfo,fd_in); + return ops_false; + } + + free (myfilename); + } + + // \todo check for suffix matching armour param + + // setup for writing decrypted contents to given output file + + // setup keyring and passphrase callback + pinfo->cbinfo.cryptinfo.keyring=keyring; + pinfo->cbinfo.cryptinfo.cb_get_passphrase=cb_get_passphrase; + + // Set up armour/passphrase options + + if (use_armour) + ops_reader_push_dearmour(pinfo); + + // Do it + + ops_parse_and_print_errors(pinfo); + + // Unsetup + + if (use_armour) + ops_reader_pop_dearmour(pinfo); + + ops_teardown_file_write(pinfo->cbinfo.cinfo, fd_out); + ops_teardown_file_read(pinfo, fd_in); + // \todo cleardown crypt + + return ops_true; + } + +static ops_parse_cb_return_t +callback_write_parsed(const ops_parser_content_t *content_,ops_parse_cb_info_t *cbinfo) + { + ops_parser_content_union_t* content=(ops_parser_content_union_t *)&content_->content; + static ops_boolean_t skipping; + // ops_boolean_t write=ops_true; + + OPS_USED(cbinfo); + +// ops_print_packet(content_); + + if(content_->tag != OPS_PTAG_CT_UNARMOURED_TEXT && skipping) + { + puts("...end of skip"); + skipping=ops_false; + } + + switch(content_->tag) + { + case OPS_PTAG_CT_UNARMOURED_TEXT: + printf("OPS_PTAG_CT_UNARMOURED_TEXT\n"); + if(!skipping) + { + puts("Skipping..."); + skipping=ops_true; + } + fwrite(content->unarmoured_text.data,1, + content->unarmoured_text.length,stdout); + break; + + case OPS_PTAG_CT_PK_SESSION_KEY: + return callback_pk_session_key(content_,cbinfo); + break; + + case OPS_PARSER_CMD_GET_SECRET_KEY: + return callback_cmd_get_secret_key(content_,cbinfo); + break; + + case OPS_PARSER_CMD_GET_SK_PASSPHRASE: + // return callback_cmd_get_secret_key_passphrase(content_,cbinfo); + return cbinfo->cryptinfo.cb_get_passphrase(content_,cbinfo); + break; + + case OPS_PTAG_CT_LITERAL_DATA_BODY: + return callback_literal_data(content_,cbinfo); + break; + + case OPS_PTAG_CT_ARMOUR_HEADER: + case OPS_PTAG_CT_ARMOUR_TRAILER: + case OPS_PTAG_CT_ENCRYPTED_PK_SESSION_KEY: + case OPS_PTAG_CT_COMPRESSED: + case OPS_PTAG_CT_LITERAL_DATA_HEADER: + case OPS_PTAG_CT_SE_IP_DATA_BODY: + case OPS_PTAG_CT_SE_IP_DATA_HEADER: + case OPS_PTAG_CT_SE_DATA_BODY: + case OPS_PTAG_CT_SE_DATA_HEADER: + + // Ignore these packets + // They're handled in ops_parse_one_packet() + // and nothing else needs to be done + break; + + default: + // return callback_general(content_,cbinfo); + break; + // fprintf(stderr,"Unexpected packet tag=%d (0x%x)\n",content_->tag, + // content_->tag); + // assert(0); + } + + return OPS_RELEASE_MEMORY; + } + +// EOF diff --git a/openpgpsdk/src/errors.c b/openpgpsdk/src/errors.c new file mode 100644 index 000000000..9ba02f4a0 --- /dev/null +++ b/openpgpsdk/src/errors.c @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + * \brief Error Handling + */ + +#include +#include + +#include +#include +#include +#include + +#ifdef WIN32 +#define vsnprintf _vsnprintf +#endif + +#include +#include + +#define ERRNAME(code) { code, #code } + +static ops_errcode_name_map_t errcode_name_map[] = + { + ERRNAME(OPS_E_OK), + ERRNAME(OPS_E_FAIL), + ERRNAME(OPS_E_SYSTEM_ERROR), + ERRNAME(OPS_E_UNIMPLEMENTED), + + ERRNAME(OPS_E_R), + ERRNAME(OPS_E_R_READ_FAILED), + ERRNAME(OPS_E_R_EARLY_EOF), + ERRNAME(OPS_E_R_BAD_FORMAT), + ERRNAME(OPS_E_R_UNCONSUMED_DATA), + + ERRNAME(OPS_E_W), + ERRNAME(OPS_E_W_WRITE_FAILED), + ERRNAME(OPS_E_W_WRITE_TOO_SHORT), + + ERRNAME(OPS_E_P), + ERRNAME(OPS_E_P_NOT_ENOUGH_DATA), + ERRNAME(OPS_E_P_UNKNOWN_TAG), + ERRNAME(OPS_E_P_PACKET_CONSUMED), + ERRNAME(OPS_E_P_MPI_FORMAT_ERROR), + + ERRNAME(OPS_E_C), + + ERRNAME(OPS_E_V), + ERRNAME(OPS_E_V_BAD_SIGNATURE), + ERRNAME(OPS_E_V_NO_SIGNATURE), + ERRNAME(OPS_E_V_UNKNOWN_SIGNER), + + ERRNAME(OPS_E_ALG), + ERRNAME(OPS_E_ALG_UNSUPPORTED_SYMMETRIC_ALG), + ERRNAME(OPS_E_ALG_UNSUPPORTED_PUBLIC_KEY_ALG), + ERRNAME(OPS_E_ALG_UNSUPPORTED_SIGNATURE_ALG), + ERRNAME(OPS_E_ALG_UNSUPPORTED_HASH_ALG), + + ERRNAME(OPS_E_PROTO), + ERRNAME(OPS_E_PROTO_BAD_SYMMETRIC_DECRYPT), + ERRNAME(OPS_E_PROTO_UNKNOWN_SS), + ERRNAME(OPS_E_PROTO_CRITICAL_SS_IGNORED), + ERRNAME(OPS_E_PROTO_BAD_PUBLIC_KEY_VRSN), + ERRNAME(OPS_E_PROTO_BAD_SIGNATURE_VRSN), + ERRNAME(OPS_E_PROTO_BAD_ONE_PASS_SIG_VRSN), + ERRNAME(OPS_E_PROTO_BAD_PKSK_VRSN), + ERRNAME(OPS_E_PROTO_DECRYPTED_MSG_WRONG_LEN), + ERRNAME(OPS_E_PROTO_BAD_SK_CHECKSUM), + + { 0x00, NULL }, /* this is the end-of-array marker */ + }; + +/** + * \ingroup Core_Errors + * \brief returns error code name + * \param errcode + * \return error code name or "Unknown" + */ +char *ops_errcode(const ops_errcode_t errcode) + { + return(ops_str_from_map((int) errcode, (ops_map_t *) errcode_name_map)); + } + +/** + * \ingroup Core_Errors + * \brief Pushes the given error on the given errorstack + * \param errstack Error stack to use + * \param errcode Code of error to push + * \param sys_errno System errno (used if errcode=OPS_E_SYSTEM_ERROR) + * \param file Source filename where error occurred + * \param line Line in source file where error occurred + * \param fmt Comment + * + */ + +void ops_push_error(ops_error_t **errstack,ops_errcode_t errcode,int sys_errno, + const char *file,int line,const char *fmt,...) + { + // first get the varargs and generate the comment + char *comment; + int maxbuf=128; + va_list args; + ops_error_t *err; + + comment=malloc(maxbuf+1); + assert(comment); + + va_start(args, fmt); + vsnprintf(comment,maxbuf+1,fmt,args); + va_end(args); + + // alloc a new error and add it to the top of the stack + + err=malloc(sizeof(ops_error_t)); + assert(err); + + err->next=*errstack; + *errstack=err; + + // fill in the details + err->errcode=errcode; + err->sys_errno=sys_errno; + err->file=file; + err->line=line; + + err->comment=comment; + } + +/** +\ingroup Core_Errors +\brief print this error +\param err Error to print +*/ +void ops_print_error(ops_error_t *err) + { + printf("%s:%d: ",err->file,err->line); + if(err->errcode==OPS_E_SYSTEM_ERROR) + printf("system error %d returned from %s()\n",err->sys_errno, + err->comment); + else + printf("%s, %s\n",ops_errcode(err->errcode),err->comment); + } + +/** +\ingroup Core_Errors +\brief Print all errors on stack +\param errstack Error stack to print +*/ +void ops_print_errors(ops_error_t *errstack) + { + ops_error_t *err; + + for(err=errstack ; err!=NULL ; err=err->next) + ops_print_error(err); + } + +/** +\ingroup Core_Errors +\brief Return true if given error is present anywhere on stack +\param errstack Error stack to check +\param errcode Error code to look for +\return 1 if found; else 0 +*/ +int ops_has_error(ops_error_t *errstack, ops_errcode_t errcode) + { + ops_error_t *err; + for (err=errstack; err!=NULL; err=err->next) + { + if (err->errcode==errcode) + return 1; + } + return 0; + } + +/** +\ingroup Core_Errors +\brief Frees all errors on stack +\param errstack Error stack to free +*/ +void ops_free_errors(ops_error_t *errstack) +{ + ops_error_t *next; + while(errstack!=NULL) { + next=errstack->next; + free(errstack->comment); + free(errstack); + errstack=next; + } +} + +// EOF diff --git a/openpgpsdk/src/fingerprint.c b/openpgpsdk/src/fingerprint.c new file mode 100644 index 000000000..2023dd8bb --- /dev/null +++ b/openpgpsdk/src/fingerprint.c @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + */ + +#include +#include +#include +#include +#include + +#include +#ifdef HAVE_ALLOCA_H +# include +#endif + +#ifdef WIN32 +#define alloca _alloca +#endif + +#include + +static int debug=0; + +/** + * \ingroup Core_Keys + * \brief Calculate a public key fingerprint. + * \param fp Where to put the calculated fingerprint + * \param key The key for which the fingerprint is calculated + */ + +void ops_fingerprint(ops_fingerprint_t *fp,const ops_public_key_t *key) + { + if(key->version == 2 || key->version == 3) + { + unsigned char *bn; + int n; + ops_hash_t md5; + + assert(key->algorithm == OPS_PKA_RSA + || key->algorithm == OPS_PKA_RSA_ENCRYPT_ONLY + || key->algorithm == OPS_PKA_RSA_SIGN_ONLY ); + + ops_hash_md5(&md5); + md5.init(&md5); + + n=BN_num_bytes(key->key.rsa.n); + bn=alloca(n); + BN_bn2bin(key->key.rsa.n,bn); + md5.add(&md5,bn,n); + + n=BN_num_bytes(key->key.rsa.e); + bn=alloca(n); + BN_bn2bin(key->key.rsa.e,bn); + md5.add(&md5,bn,n); + + md5.finish(&md5,fp->fingerprint); + fp->length=16; + } + else + { + ops_memory_t *mem=ops_memory_new(); + ops_hash_t sha1; + size_t l; + + ops_build_public_key(mem,key,ops_false); + + if (debug) + { fprintf(stderr,"--- creating key fingerprint\n"); } + + ops_hash_sha1(&sha1); + sha1.init(&sha1); + + l=ops_memory_get_length(mem); + + ops_hash_add_int(&sha1,0x99,1); + ops_hash_add_int(&sha1,l,2); + sha1.add(&sha1,ops_memory_get_data(mem),l); + sha1.finish(&sha1,fp->fingerprint); + + if (debug) + { fprintf(stderr,"--- finished creating key fingerprint\n"); } + + fp->length=20; + + ops_memory_free(mem); + } + } + +/** + * \ingroup Core_Keys + * \brief Calculate the Key ID from the public key. + * \param keyid Space for the calculated ID to be stored + * \param key The key for which the ID is calculated + */ + +void ops_keyid(unsigned char keyid[8],const ops_public_key_t *key) + { + if(key->version == 2 || key->version == 3) + { + unsigned char bn[8192]; + unsigned n=BN_num_bytes(key->key.rsa.n); + + assert(n <= sizeof bn); + assert(key->algorithm == OPS_PKA_RSA + || key->algorithm == OPS_PKA_RSA_ENCRYPT_ONLY + || key->algorithm == OPS_PKA_RSA_SIGN_ONLY ); + BN_bn2bin(key->key.rsa.n,bn); + memcpy(keyid,bn+n-8,8); + } + else + { + ops_fingerprint_t fingerprint; + + ops_fingerprint(&fingerprint,key); + memcpy(keyid,fingerprint.fingerprint+fingerprint.length-8,8); + } + } + +// EOF diff --git a/openpgpsdk/src/hash.c b/openpgpsdk/src/hash.c new file mode 100644 index 000000000..5154f46ac --- /dev/null +++ b/openpgpsdk/src/hash.c @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + */ + +#include +#include +#include + +#include + +static int debug=0; + +/** +\ingroup Core_Hashes +\brief Add to the hash +\param hash Hash to add to +\param n Int to add +\param length Length of int in bytes +*/ +void ops_hash_add_int(ops_hash_t *hash,unsigned n,unsigned length) + { + while(length--) + { + unsigned char c[1]; + + c[0]=n >> (length*8); + hash->add(hash,c,1); + } + } + +/** +\ingroup Core_Hashes +\brief Setup hash for given hash algorithm +\param hash Hash to set up +\param alg Hash algorithm to use +*/ +void ops_hash_any(ops_hash_t *hash,ops_hash_algorithm_t alg) + { + switch(alg) + { + case OPS_HASH_MD5: + ops_hash_md5(hash); + break; + + case OPS_HASH_SHA1: + ops_hash_sha1(hash); + break; + + case OPS_HASH_SHA256: + ops_hash_sha256(hash); + break; + + case OPS_HASH_SHA384: + ops_hash_sha384(hash); + break; + + case OPS_HASH_SHA512: + ops_hash_sha512(hash); + break; + + case OPS_HASH_SHA224: + ops_hash_sha224(hash); + break; + + default: + assert(0); + } + } + +/** +\ingroup Core_Hashes +\brief Returns size of hash for given hash algorithm +\param alg Hash algorithm to use +\return Size of hash algorithm in bytes +*/ +unsigned ops_hash_size(ops_hash_algorithm_t alg) + { + switch(alg) + { + case OPS_HASH_MD5: + return 16; + + case OPS_HASH_SHA1: + return 20; + + case OPS_HASH_SHA256: + return 32; + + case OPS_HASH_SHA224: + return 28; + + case OPS_HASH_SHA512: + return 64; + + case OPS_HASH_SHA384: + return 48; + + default: + assert(0); + } + + return 0; + } + +/** +\ingroup Core_Hashes +\brief Returns hash enum corresponding to given string +\param hash Text name of hash algorithm i.e. "SHA1" +\returns Corresponding enum i.e. OPS_HASH_SHA1 +*/ +ops_hash_algorithm_t ops_hash_algorithm_from_text(const char *hash) + { + if(!strcmp(hash,"SHA1")) + return OPS_HASH_SHA1; + else if(!strcmp(hash,"MD5")) + return OPS_HASH_MD5; + else if (!strcmp(hash,"SHA256")) + return OPS_HASH_SHA256; + /* + else if (!strcmp(hash,"SHA224")) + return OPS_HASH_SHA224; + */ + else if (!strcmp(hash,"SHA512")) + return OPS_HASH_SHA512; + else if (!strcmp(hash,"SHA384")) + return OPS_HASH_SHA384; + + return OPS_HASH_UNKNOWN; + } + +/** +\ingroup Core_Hashes +\brief Hash given data +\param out Where to write the hash +\param alg Hash algorithm to use +\param in Data to hash +\param length Length of data +\return Size of hash created +*/ +unsigned ops_hash(unsigned char *out,ops_hash_algorithm_t alg,const void *in, + size_t length) + { + ops_hash_t hash; + + ops_hash_any(&hash,alg); + hash.init(&hash); + hash.add(&hash,in,length); + return hash.finish(&hash,out); + } + +/** +\ingroup Core_Hashes +\brief Calculate hash for MDC packet +\param preamble Preamble to hash +\param sz_preamble Size of preamble +\param plaintext Plaintext to hash +\param sz_plaintext Size of plaintext +\param hashed Resulting hash +*/ +void ops_calc_mdc_hash(const unsigned char* preamble, const size_t sz_preamble, const unsigned char* plaintext, const unsigned int sz_plaintext, unsigned char *hashed) + { + ops_hash_t hash; + unsigned char c[1]; + + if (debug) + { + unsigned int i=0; + fprintf(stderr,"ops_calc_mdc_hash():\n"); + + fprintf(stderr,"\npreamble: "); + for (i=0; i +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "keyring_local.h" +#include "parse_local.h" + +#include +#include +#ifndef WIN32 +#include +#include +#endif +#include +#include + +#include + +/** + \ingroup HighLevel_Keyring + + \brief Creates a new ops_keydata_t struct + + \return A new ops_keydata_t struct, initialised to zero. + + \note The returned ops_keydata_t struct must be freed after use with ops_keydata_free. +*/ + +ops_keydata_t *ops_keydata_new(void) + { return ops_mallocz(sizeof(ops_keydata_t)); } + + +/** + \ingroup HighLevel_Keyring + + \brief Frees keydata and its memory + + \param keydata Key to be freed. + + \note This frees the keydata itself, as well as any other memory alloc-ed by it. +*/ +void ops_keydata_free(ops_keydata_t *keydata) + { + unsigned n; + + for(n=0 ; n < keydata->nuids ; ++n) + ops_user_id_free(&keydata->uids[n]); + free(keydata->uids); + keydata->uids=NULL; + keydata->nuids=0; + + for(n=0 ; n < keydata->npackets ; ++n) + ops_packet_free(&keydata->packets[n]); + free(keydata->packets); + keydata->packets=NULL; + keydata->npackets=0; + + if(keydata->type == OPS_PTAG_CT_PUBLIC_KEY) + ops_public_key_free(&keydata->key.pkey); + else + ops_secret_key_free(&keydata->key.skey); + + free(keydata); + } + +/** + \ingroup HighLevel_KeyGeneral + + \brief Returns the public key in the given keydata. + \param keydata + + \return Pointer to public key + + \note This is not a copy, do not free it after use. +*/ + +const ops_public_key_t * +ops_get_public_key_from_data(const ops_keydata_t *keydata) + { + if(keydata->type == OPS_PTAG_CT_PUBLIC_KEY) + return &keydata->key.pkey; + return &keydata->key.skey.public_key; + } + +/** +\ingroup HighLevel_KeyGeneral + +\brief Check whether this is a secret key or not. +*/ + +ops_boolean_t ops_is_key_secret(const ops_keydata_t *data) + { return data->type != OPS_PTAG_CT_PUBLIC_KEY; } + +/** + \ingroup HighLevel_KeyGeneral + + \brief Returns the secret key in the given keydata. + + \note This is not a copy, do not free it after use. + + \note This returns a const. If you need to be able to write to this pointer, use ops_get_writable_secret_key_from_data +*/ + +const ops_secret_key_t * +ops_get_secret_key_from_data(const ops_keydata_t *data) + { + if(data->type != OPS_PTAG_CT_SECRET_KEY) + return NULL; + + return &data->key.skey; + } + +/** + \ingroup HighLevel_KeyGeneral + + \brief Returns the secret key in the given keydata. + + \note This is not a copy, do not free it after use. + + \note If you do not need to be able to modify this key, there is an equivalent read-only function ops_get_secret_key_from_data. +*/ + +ops_secret_key_t * +ops_get_writable_secret_key_from_data(ops_keydata_t *data) + { + if (data->type != OPS_PTAG_CT_SECRET_KEY) + return NULL; + + return &data->key.skey; + } + +typedef struct + { + const ops_keydata_t *key; + char *pphrase; + ops_secret_key_t *skey; + } decrypt_arg_t; + +static ops_parse_cb_return_t decrypt_cb(const ops_parser_content_t *content_, + ops_parse_cb_info_t *cbinfo) + { + const ops_parser_content_union_t *content=&content_->content; + decrypt_arg_t *arg=ops_parse_cb_get_arg(cbinfo); + + OPS_USED(cbinfo); + + switch(content_->tag) + { + case OPS_PARSER_PTAG: + case OPS_PTAG_CT_USER_ID: + case OPS_PTAG_CT_SIGNATURE: + case OPS_PTAG_CT_SIGNATURE_HEADER: + case OPS_PTAG_CT_SIGNATURE_FOOTER: + case OPS_PTAG_CT_TRUST: + break; + + case OPS_PARSER_CMD_GET_SK_PASSPHRASE: + *content->secret_key_passphrase.passphrase=arg->pphrase; + return OPS_KEEP_MEMORY; + + case OPS_PARSER_ERRCODE: + switch(content->errcode.errcode) + { + case OPS_E_P_MPI_FORMAT_ERROR: + /* Generally this means a bad passphrase */ + fprintf(stderr,"Bad passphrase!\n"); + goto done; + + case OPS_E_P_PACKET_CONSUMED: + /* And this is because of an error we've accepted */ + goto done; + + default: + fprintf(stderr,"parse error: %s\n", + ops_errcode(content->errcode.errcode)); + assert(0); + break; + } + + break; + + case OPS_PARSER_ERROR: + printf("parse error: %s\n",content->error.error); + assert(0); + break; + + case OPS_PTAG_CT_SECRET_KEY: + arg->skey=malloc(sizeof *arg->skey); + *arg->skey=content->secret_key; + return OPS_KEEP_MEMORY; + + case OPS_PARSER_PACKET_END: + // nothing to do + break; + + default: + fprintf(stderr,"Unexpected tag %d (0x%x)\n",content_->tag, + content_->tag); + assert(0); + } + + done: + return OPS_RELEASE_MEMORY; + } + +/** +\ingroup Core_Keys +\brief Decrypts secret key from given keydata with given passphrase +\param key Key from which to get secret key +\param pphrase Passphrase to use to decrypt secret key +\return secret key +*/ +ops_secret_key_t *ops_decrypt_secret_key_from_data(const ops_keydata_t *key, + const char *pphrase) + { + ops_parse_info_t *pinfo; + decrypt_arg_t arg; + + memset(&arg,'\0',sizeof arg); + arg.key=key; + arg.pphrase=strdup(pphrase); + + pinfo=ops_parse_info_new(); + + ops_keydata_reader_set(pinfo,key); + ops_parse_cb_set(pinfo,decrypt_cb,&arg); + pinfo->rinfo.accumulate=ops_true; + + ops_parse(pinfo); + + return arg.skey; + } + +/** +\ingroup Core_Keys +\brief Set secret key in content +\param content Content to be set +\param key Keydata to get secret key from +*/ +void ops_set_secret_key(ops_parser_content_union_t* content,const ops_keydata_t *key) + { + *content->get_secret_key.secret_key=&key->key.skey; + } + +/** +\ingroup Core_Keys +\brief Get Key ID from keydata +\param key Keydata to get Key ID from +\return Pointer to Key ID inside keydata +*/ +const unsigned char* ops_get_key_id(const ops_keydata_t *key) + { + return key->key_id; + } + +/** +\ingroup Core_Keys +\brief How many User IDs in this key? +\param key Keydata to check +\return Num of user ids +*/ +unsigned ops_get_user_id_count(const ops_keydata_t *key) + { + return key->nuids; + } + +/** +\ingroup Core_Keys +\brief Get indexed user id from key +\param key Key to get user id from +\param index Which key to get +\return Pointer to requested user id +*/ +const unsigned char* ops_get_user_id(const ops_keydata_t *key, unsigned index) + { + return key->uids[index].user_id; + } + +/** + \ingroup HighLevel_Supported + \brief Checks whether key's algorithm and type are supported by OpenPGP::SDK + \param keydata Key to be checked + \return ops_true if key algorithm and type are supported by OpenPGP::SDK; ops_false if not +*/ + +ops_boolean_t ops_is_key_supported(const ops_keydata_t *keydata) + { + if ( keydata->type == OPS_PTAG_CT_PUBLIC_KEY ) { + if ( keydata->key.pkey.algorithm == OPS_PKA_RSA ) { + return ops_true; + } + } else if ( keydata->type == OPS_PTAG_CT_PUBLIC_KEY ) { + if ( keydata->key.skey.algorithm == (ops_symmetric_algorithm_t)OPS_PKA_RSA ) { + return ops_true; + } + } + return ops_false; + } + + +/** + \ingroup HighLevel_KeyringFind + + \brief Returns key inside a keyring, chosen by index + + \param keyring Pointer to existing keyring + \param index Index of required key + + \note Index starts at 0 + + \note This returns a pointer to the original key, not a copy. You do not need to free the key after use. + + \return Pointer to the required key; or NULL if index too large. + + Example code: + \code + void example(const ops_keyring_t* keyring) + { + ops_keydata_t* keydata=NULL; + keydata=ops_keyring_get_key_by_index(keyring, 0); + ... + } + \endcode +*/ + +const ops_keydata_t* ops_keyring_get_key_by_index(const ops_keyring_t *keyring, int index) + { + if (index >= keyring->nkeys) + return NULL; + return &keyring->keys[index]; + } + +// \todo check where userid pointers are copied +/** +\ingroup Core_Keys +\brief Copy user id, including contents +\param dst Destination User ID +\param src Source User ID +\note If dst already has a user_id, it will be freed. +*/ +void ops_copy_userid(ops_user_id_t* dst, const ops_user_id_t* src) + { + int len=strlen((char *)src->user_id); + if (dst->user_id) + free(dst->user_id); + dst->user_id=ops_mallocz(len+1); + + memcpy(dst->user_id, src->user_id, len); + } + +// \todo check where pkt pointers are copied +/** +\ingroup Core_Keys +\brief Copy packet, including contents +\param dst Destination packet +\param src Source packet +\note If dst already has a packet, it will be freed. +*/ +void ops_copy_packet(ops_packet_t* dst, const ops_packet_t* src) + { + if (dst->raw) + free(dst->raw); + dst->raw=ops_mallocz(src->length); + + dst->length=src->length; + memcpy(dst->raw, src->raw, src->length); + } + +/** +\ingroup Core_Keys +\brief Add User ID to keydata +\param keydata Key to which to add User ID +\param userid User ID to add +\return Pointer to new User ID +*/ +ops_user_id_t* ops_add_userid_to_keydata(ops_keydata_t* keydata, const ops_user_id_t* userid) + { + ops_user_id_t* new_uid=NULL; + + EXPAND_ARRAY(keydata, uids); + + // initialise new entry in array + new_uid=&keydata->uids[keydata->nuids]; + + new_uid->user_id=NULL; + + // now copy it + ops_copy_userid(new_uid,userid); + keydata->nuids++; + + return new_uid; + } + +/** +\ingroup Core_Keys +\brief Add packet to key +\param keydata Key to which to add packet +\param packet Packet to add +\return Pointer to new packet +*/ +ops_packet_t* ops_add_packet_to_keydata(ops_keydata_t* keydata, const ops_packet_t* packet) + { + ops_packet_t* new_pkt=NULL; + + EXPAND_ARRAY(keydata, packets); + + // initialise new entry in array + new_pkt=&keydata->packets[keydata->npackets]; + new_pkt->length=0; + new_pkt->raw=NULL; + + // now copy it + ops_copy_packet(new_pkt, packet); + keydata->npackets++; + + return new_pkt; + } + +/** +\ingroup Core_Keys +\brief Add signed User ID to key +\param keydata Key to which to add signed User ID +\param user_id User ID to add +\param sigpacket Packet to add +*/ +void ops_add_signed_userid_to_keydata(ops_keydata_t* keydata, const ops_user_id_t* user_id, const ops_packet_t* sigpacket) + { + //int i=0; + ops_user_id_t * uid=NULL; + ops_packet_t * pkt=NULL; + + uid=ops_add_userid_to_keydata(keydata, user_id); + pkt=ops_add_packet_to_keydata(keydata, sigpacket); + + /* + * add entry in sigs array to link the userid and sigpacket + */ + + // and add ptr to it from the sigs array + EXPAND_ARRAY(keydata, sigs); + + // setup new entry in array + + keydata->sigs[keydata->nsigs].userid=uid; + keydata->sigs[keydata->nsigs].packet=pkt; + + keydata->nsigs++; + } + +/** +\ingroup Core_Keys +\brief Add selfsigned User ID to key +\param keydata Key to which to add user ID +\param userid Self-signed User ID to add +\return ops_true if OK; else ops_false +*/ +ops_boolean_t ops_add_selfsigned_userid_to_keydata(ops_keydata_t* keydata, ops_user_id_t* userid) + { + ops_packet_t sigpacket; + + ops_memory_t* mem_userid=NULL; + ops_create_info_t* cinfo_userid=NULL; + + ops_memory_t* mem_sig=NULL; + ops_create_info_t* cinfo_sig=NULL; + + ops_create_signature_t *sig=NULL; + + /* + * create signature packet for this userid + */ + + // create userid pkt + ops_setup_memory_write(&cinfo_userid, &mem_userid, 128); + ops_write_struct_user_id(userid, cinfo_userid); + + // create sig for this pkt + + sig=ops_create_signature_new(); + ops_signature_start_key_signature(sig, &keydata->key.skey.public_key, userid, OPS_CERT_POSITIVE); + ops_signature_add_creation_time(sig,time(NULL)); + ops_signature_add_issuer_key_id(sig,keydata->key_id); + ops_signature_add_primary_user_id(sig, ops_true); + ops_signature_hashed_subpackets_end(sig); + + ops_setup_memory_write(&cinfo_sig, &mem_sig, 128); + ops_write_signature(sig,&keydata->key.skey.public_key,&keydata->key.skey, cinfo_sig); + + // add this packet to keydata + + sigpacket.length=ops_memory_get_length(mem_sig); + sigpacket.raw=ops_memory_get_data(mem_sig); + + // add userid to keydata + ops_add_signed_userid_to_keydata(keydata, userid, &sigpacket); + + // cleanup + ops_create_signature_delete(sig); + ops_create_info_delete(cinfo_userid); + ops_create_info_delete(cinfo_sig); + ops_memory_free(mem_userid); + ops_memory_free(mem_sig); + + return ops_true; + } + +/** +\ingroup Core_Keys +\brief Initialise ops_keydata_t +\param keydata Keydata to initialise +\param type OPS_PTAG_CT_PUBLIC_KEY or OPS_PTAG_CT_SECRET_KEY +*/ +void ops_keydata_init(ops_keydata_t* keydata, const ops_content_tag_t type) + { + assert(keydata->type==OPS_PTAG_CT_RESERVED); + assert(type==OPS_PTAG_CT_PUBLIC_KEY || type==OPS_PTAG_CT_SECRET_KEY); + + keydata->type=type; + } + +/** + Example Usage: + \code + + // definition of variables + ops_keyring_t keyring; + char* filename="~/.gnupg/pubring.gpg"; + + // Read keyring from file + ops_keyring_read_from_file(&keyring,filename); + + // do actions using keyring + ... + + // Free memory alloc-ed in ops_keyring_read_from_file() + ops_keyring_free(keyring); + \endcode +*/ + +static ops_parse_cb_return_t +cb_keyring_read(const ops_parser_content_t *content_, + ops_parse_cb_info_t *cbinfo); + +/** + \ingroup HighLevel_KeyringRead + + \brief Reads a keyring from a file + + \param keyring Pointer to an existing ops_keyring_t struct + \param armour ops_true if file is armoured; else ops_false + \param filename Filename of keyring to be read + + \return ops true if OK; ops_false on error + + \note Keyring struct must already exist. + + \note Can be used with either a public or secret keyring. + + \note You must call ops_keyring_free() after usage to free alloc-ed memory. + + \note If you call this twice on the same keyring struct, without calling + ops_keyring_free() between these calls, you will introduce a memory leak. + + \sa ops_keyring_read_from_mem() + \sa ops_keyring_free() + + Example code: + \code + ops_keyring_t* keyring=ops_mallocz(sizeof *keyring); + ops_boolean_t armoured=ops_false; + ops_keyring_read_from_file(keyring, armoured, "~/.gnupg/pubring.gpg"); + ... + ops_keyring_free(keyring); + free (keyring); + + \endcode +*/ + +ops_boolean_t ops_keyring_read_from_file(ops_keyring_t *keyring, const ops_boolean_t armour, const char *filename) + { + ops_parse_info_t *pinfo; + int fd; + ops_boolean_t res = ops_true; + + pinfo=ops_parse_info_new(); + + // add this for the moment, + // \todo need to fix the problems with reading signature subpackets later + + // ops_parse_options(pinfo,OPS_PTAG_SS_ALL,OPS_PARSE_RAW); + ops_parse_options(pinfo,OPS_PTAG_SS_ALL,OPS_PARSE_PARSED); + +#ifdef WIN32 + fd=open(filename,O_RDONLY|O_BINARY); +#else + fd=open(filename,O_RDONLY); +#endif + if(fd < 0) + { + ops_parse_info_delete(pinfo); + perror(filename); + return ops_false; + } + + ops_reader_set_fd(pinfo,fd); + + ops_parse_cb_set(pinfo,cb_keyring_read,NULL); + + if (armour) + { ops_reader_push_dearmour(pinfo); } + + if ( ops_parse_and_accumulate(keyring,pinfo) == 0 ) { + res = ops_false; + } + else + { + res = ops_true; + } + ops_print_errors(ops_parse_info_get_errors(pinfo)); + + if (armour) + ops_reader_pop_dearmour(pinfo); + + close(fd); + + ops_parse_info_delete(pinfo); + + return res; + } + +/** + \ingroup HighLevel_KeyringRead + + \brief Reads a keyring from memory + + \param keyring Pointer to existing ops_keyring_t struct + \param armour ops_true if file is armoured; else ops_false + \param mem Pointer to a ops_memory_t struct containing keyring to be read + + \return ops true if OK; ops_false on error + + \note Keyring struct must already exist. + + \note Can be used with either a public or secret keyring. + + \note You must call ops_keyring_free() after usage to free alloc-ed memory. + + \note If you call this twice on the same keyring struct, without calling + ops_keyring_free() between these calls, you will introduce a memory leak. + + \sa ops_keyring_read_from_file + \sa ops_keyring_free + + Example code: + \code + ops_memory_t* mem; // Filled with keyring packets + ops_keyring_t* keyring=ops_mallocz(sizeof *keyring); + ops_boolean_t armoured=ops_false; + ops_keyring_read_from_mem(keyring, armoured, mem); + ... + ops_keyring_free(keyring); + free (keyring); + \endcode +*/ +ops_boolean_t ops_keyring_read_from_mem(ops_keyring_t *keyring, const ops_boolean_t armour, ops_memory_t* mem) + { + ops_parse_info_t *pinfo=NULL; + ops_boolean_t res = ops_true; + + pinfo=ops_parse_info_new(); + ops_parse_options(pinfo,OPS_PTAG_SS_ALL,OPS_PARSE_PARSED); + + ops_setup_memory_read(&pinfo, mem, NULL, cb_keyring_read, OPS_ACCUMULATE_NO); + + if (armour) + { ops_reader_push_dearmour(pinfo); } + + if ( ops_parse_and_accumulate(keyring,pinfo) == 0 ) + { + res = ops_false; + } + else + { + res = ops_true; + } + ops_print_errors(ops_parse_info_get_errors(pinfo)); + + if (armour) + ops_reader_pop_dearmour(pinfo); + + // don't call teardown_memory_read because memory was passed in + ops_parse_info_delete(pinfo); + + return res; + } + +/** + \ingroup HighLevel_KeyringRead + + \brief Frees keyring's contents (but not keyring itself) + + \param keyring Keyring whose data is to be freed + + \note This does not free keyring itself, just the memory alloc-ed in it. + */ +void ops_keyring_free(ops_keyring_t *keyring) + { + free(keyring->keys); + keyring->keys=NULL; + keyring->nkeys=0; + keyring->nkeys_allocated=0; + } + +/** + \ingroup HighLevel_KeyringFind + + \brief Finds key in keyring from its Key ID + + \param keyring Keyring to be searched + \param keyid ID of required key + + \return Pointer to key, if found; NULL, if not found + + \note This returns a pointer to the key inside the given keyring, not a copy. Do not free it after use. + + Example code: + \code + void example(ops_keyring_t* keyring) + { + ops_keydata_t* keydata=NULL; + unsigned char keyid[OPS_KEY_ID_SIZE]; // value set elsewhere + keydata=ops_keyring_find_key_by_id(keyring,keyid); + ... + } + \endcode +*/ +const ops_keydata_t * +ops_keyring_find_key_by_id(const ops_keyring_t *keyring, + const unsigned char keyid[OPS_KEY_ID_SIZE]) + { + int n; + + if (!keyring) + return NULL; + + for(n=0 ; n < keyring->nkeys ; ++n) + { + if(!memcmp(keyring->keys[n].key_id,keyid,OPS_KEY_ID_SIZE)) + return &keyring->keys[n]; + } + + return NULL; + } + +/** + \ingroup HighLevel_KeyringFind + + \brief Finds key from its User ID + + \param keyring Keyring to be searched + \param userid User ID of required key + + \return Pointer to Key, if found; NULL, if not found + + \note This returns a pointer to the key inside the keyring, not a copy. Do not free it. + + Example code: + \code + void example(ops_keyring_t* keyring) + { + ops_keydata_t* keydata=NULL; + keydata=ops_keyring_find_key_by_userid(keyring,"user@domain.com"); + ... + } + \endcode +*/ +const ops_keydata_t * +ops_keyring_find_key_by_userid(const ops_keyring_t *keyring, + const char *userid) + { + int n=0; + unsigned int i=0; + + if (!keyring) + return NULL; + + for(n=0 ; n < keyring->nkeys ; ++n) + { + for(i=0; ikeys[n].nuids; i++) + { + //printf("[%d][%d] userid %s\n",n,i,keyring->keys[n].uids[i].user_id); + if(!strncmp((char *)keyring->keys[n].uids[i].user_id,userid,strlen(userid))) + return &keyring->keys[n]; + } + } + + //printf("end: n=%d,i=%d\n",n,i); + return NULL; + } + +/** + \ingroup HighLevel_KeyringList + + \brief Prints all keys in keyring to stdout. + + \param keyring Keyring to use + + \return none + + Example code: + \code + void example() + { + ops_keyring_t* keyring=ops_mallocz(sizeof *keyring); + ops_boolean_t armoured=ops_false; + ops_keyring_read_from_file(keyring, armoured, "~/.gnupg/pubring.gpg"); + + ops_keyring_list(keyring); + + ops_keyring_free(keyring); + free (keyring); + } + \endcode +*/ + +void +ops_keyring_list(const ops_keyring_t* keyring) + { + int n; + unsigned int i; + ops_keydata_t* key; + + printf ("%d keys\n", keyring->nkeys); + for(n=0,key=&keyring->keys[n] ; n < keyring->nkeys ; ++n,++key) + { + for(i=0; inuids; i++) + { + if (ops_is_key_secret(key)) + ops_print_secret_keydata(key); + else + ops_print_public_keydata(key); + } + + } + } + +/* Static functions */ + +static ops_parse_cb_return_t +cb_keyring_read(const ops_parser_content_t *content_, + ops_parse_cb_info_t *cbinfo) + { + OPS_USED(cbinfo); + + switch(content_->tag) + { + case OPS_PARSER_PTAG: + case OPS_PTAG_CT_ENCRYPTED_SECRET_KEY: // we get these because we didn't prompt + case OPS_PTAG_CT_SIGNATURE_HEADER: + case OPS_PTAG_CT_SIGNATURE_FOOTER: + case OPS_PTAG_CT_SIGNATURE: + case OPS_PTAG_CT_TRUST: + case OPS_PARSER_ERRCODE: + break; + + default: + ; + } + + return OPS_RELEASE_MEMORY; + } + +/*\@}*/ + +// eof diff --git a/openpgpsdk/src/keyring_local.h b/openpgpsdk/src/keyring_local.h new file mode 100644 index 000000000..5c3860f35 --- /dev/null +++ b/openpgpsdk/src/keyring_local.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + */ + +#include + +#define DECLARE_ARRAY(type,arr) unsigned n##arr; unsigned n##arr##_allocated; type *arr +#define EXPAND_ARRAY(str,arr) do if(str->n##arr == str->n##arr##_allocated) \ + { \ + str->n##arr##_allocated=str->n##arr##_allocated*2+10; \ + str->arr=realloc(str->arr,str->n##arr##_allocated*sizeof *str->arr); \ + } while(0) + +/** ops_keydata_key_t + */ +typedef union + { + ops_public_key_t pkey; + ops_secret_key_t skey; + } ops_keydata_key_t; + + +/** sigpacket_t */ +typedef struct + { + ops_user_id_t* userid; + ops_packet_t* packet; + } sigpacket_t; + +// XXX: gonna have to expand this to hold onto subkeys, too... +/** \struct ops_keydata + * \todo expand to hold onto subkeys + */ +struct ops_keydata + { + DECLARE_ARRAY(ops_user_id_t,uids); + DECLARE_ARRAY(ops_packet_t,packets); + DECLARE_ARRAY(sigpacket_t, sigs); + unsigned char key_id[8]; + ops_fingerprint_t fingerprint; + ops_content_tag_t type; + ops_keydata_key_t key; + }; diff --git a/openpgpsdk/src/lists.c b/openpgpsdk/src/lists.c new file mode 100644 index 000000000..5be1e2e2c --- /dev/null +++ b/openpgpsdk/src/lists.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file + * + * Set of functions to manage a dynamic list + */ + +#include + +#include + +#include + +/** + * \ingroup Core_Lists + * \brief Initialises ulong list + * \param *list Pointer to existing list structure + */ +void ops_ulong_list_init(ops_ulong_list_t *list) + { + list->size=0; + list->used=0; + list->ulongs=NULL; + } + +/** + * \ingroup Core_Lists + * \brief Frees allocated memory in ulong list. Does not free *list itself. + * \param *list + */ +void ops_ulong_list_free(ops_ulong_list_t *list) + { + if (list->ulongs) + free(list->ulongs); + ops_ulong_list_init(list); + } + +/** + * \ingroup Core_Lists + * \brief Resizes ulong list. + * + * We only resize in one direction - upwards. + * Algorithm used : double the current size then add 1 + * + * \param *list Pointer to list + * \return 1 if success, else 0 + */ + +static unsigned int ops_ulong_list_resize(ops_ulong_list_t *list) + { + + int newsize=0; + + newsize=list->size*2 + 1; + list->ulongs=realloc(list->ulongs,newsize*sizeof *list->ulongs); + if (list->ulongs) + { + list->size=newsize; + return 1; + } + else + { + /* xxx - realloc failed. error message? - rachel */ + return 0; + } + } + +/** + * \ingroup Core_Lists + * Adds entry to ulong list + * + * \param *list + * \param *ulong + * + * \return 1 if success, else 0 + */ +unsigned int ops_ulong_list_add(ops_ulong_list_t *list, unsigned long *ulong) + { + if (list->size==list->used) + if (!ops_ulong_list_resize(list)) + return 0; + + list->ulongs[list->used]=*ulong; + list->used++; + return 1; + } + diff --git a/openpgpsdk/src/memory.c b/openpgpsdk/src/memory.c new file mode 100644 index 000000000..4093e5657 --- /dev/null +++ b/openpgpsdk/src/memory.c @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + */ + +#include +#include +#include +#include + +#include + +struct ops_memory + { + unsigned char *buf; + size_t length; + size_t allocated; + }; + +/** +\ingroup HighLevel_Memory +\brief Memory to initialise +\param mem memory to initialise +\param initial_size Size to initialise to +*/ +void ops_memory_init(ops_memory_t *mem,size_t initial_size) + { + mem->length=0; + if(mem->buf) + { + if(mem->allocated < initial_size) + { + mem->buf=realloc(mem->buf,initial_size); + mem->allocated=initial_size; + } + return; + } + mem->buf=malloc(initial_size); + mem->allocated=initial_size; + } + +/** +\ingroup HighLevel_Memory +\brief Pad memory to required length +\param mem Memory to use +\param length New size +*/ +void ops_memory_pad(ops_memory_t *mem,size_t length) + { + assert(mem->allocated >= mem->length); + if(mem->allocated < mem->length+length) + { + mem->allocated=mem->allocated*2+length; + mem->buf=realloc(mem->buf,mem->allocated); + } + assert(mem->allocated >= mem->length+length); + } + +/** +\ingroup HighLevel_Memory +\brief Add data to memory +\param mem Memory to which to add +\param src Data to add +\param length Length of data to add +*/ +void ops_memory_add(ops_memory_t *mem,const unsigned char *src,size_t length) + { + ops_memory_pad(mem,length); + memcpy(mem->buf+mem->length,src,length); + mem->length+=length; + } + +// XXX: this could be refactored via the writer, but an awful lot of +// hoops to jump through for 2 lines of code! +void ops_memory_place_int(ops_memory_t *mem,unsigned offset,unsigned n, + size_t length) + { + assert(mem->allocated >= offset+length); + + while(length--) + mem->buf[offset++]=n >> (length*8); + } + +/** + * \ingroup HighLevel_Memory + * \brief Retains allocated memory and set length of stored data to zero. + * \param mem Memory to clear + * \sa ops_memory_release() + * \sa ops_memory_free() + */ +void ops_memory_clear(ops_memory_t *mem) + { mem->length=0; } + +/** +\ingroup HighLevel_Memory +\brief Free memory and associated data +\param mem Memory to free +\note This does not free mem itself +\sa ops_memory_clear() +\sa ops_memory_free() +*/ +void ops_memory_release(ops_memory_t *mem) + { + free(mem->buf); + mem->buf=NULL; + mem->length=0; + } + +void ops_memory_make_packet(ops_memory_t *out,ops_content_tag_t tag) + { + size_t extra; + + if(out->length < 192) + extra=1; + else if(out->length < 8384) + extra=2; + else + extra=5; + + ops_memory_pad(out,extra+1); + memmove(out->buf+extra+1,out->buf,out->length); + + out->buf[0]=OPS_PTAG_ALWAYS_SET|OPS_PTAG_NEW_FORMAT|tag; + + if(out->length < 192) + out->buf[1]=out->length; + else if(out->length < 8384) + { + out->buf[1]=((out->length-192) >> 8)+192; + out->buf[2]=out->length-192; + } + else + { + out->buf[1]=0xff; + out->buf[2]=out->length >> 24; + out->buf[3]=out->length >> 16; + out->buf[4]=out->length >> 8; + out->buf[5]=out->length; + } + + out->length+=extra+1; + } + +/** + \ingroup HighLevel_Memory + \brief Create a new zeroed ops_memory_t + \return Pointer to new ops_memory_t + \note Free using ops_memory_free() after use. + \sa ops_memory_free() +*/ + +ops_memory_t *ops_memory_new() + { return ops_mallocz(sizeof(ops_memory_t)); } + +/** + \ingroup HighLevel_Memory + \brief Free memory ptr and associated memory + \param mem Memory to be freed + \sa ops_memory_release() + \sa ops_memory_clear() +*/ + +void ops_memory_free(ops_memory_t *mem) + { + ops_memory_release(mem); + free(mem); + } + +/** + \ingroup HighLevel_Memory + \brief Get length of data stored in ops_memory_t struct + \return Number of bytes in data +*/ +size_t ops_memory_get_length(const ops_memory_t *mem) + { return mem->length; } + +/** + \ingroup HighLevel_Memory + \brief Get data stored in ops_memory_t struct + \return Pointer to data +*/ +void *ops_memory_get_data(ops_memory_t *mem) + { return mem->buf; } + +// EOF diff --git a/openpgpsdk/src/openssl_crypto.c b/openpgpsdk/src/openssl_crypto.c new file mode 100644 index 000000000..c45c68491 --- /dev/null +++ b/openpgpsdk/src/openssl_crypto.c @@ -0,0 +1,792 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "keyring_local.h" +#include + +#include + +static int debug=0; + +void test_secret_key(const ops_secret_key_t *skey) + { + RSA* test=RSA_new(); + + test->n=BN_dup(skey->public_key.key.rsa.n); + test->e=BN_dup(skey->public_key.key.rsa.e); + + test->d=BN_dup(skey->key.rsa.d); + test->p=BN_dup(skey->key.rsa.p); + test->q=BN_dup(skey->key.rsa.q); + + assert(RSA_check_key(test)==1); + RSA_free(test); + } + +static void md5_init(ops_hash_t *hash) + { + assert(!hash->data); + hash->data=malloc(sizeof(MD5_CTX)); + MD5_Init(hash->data); + } + +static void md5_add(ops_hash_t *hash,const unsigned char *data,unsigned length) + { + MD5_Update(hash->data,data,length); + } + +static unsigned md5_finish(ops_hash_t *hash,unsigned char *out) + { + MD5_Final(out,hash->data); + free(hash->data); + hash->data=NULL; + return 16; + } + +static ops_hash_t md5={OPS_HASH_MD5,MD5_DIGEST_LENGTH,"MD5",md5_init,md5_add, + md5_finish,NULL}; + +/** + \ingroup Core_Crypto + \brief Initialise to MD5 + \param hash Hash to initialise +*/ +void ops_hash_md5(ops_hash_t *hash) + { + *hash=md5; + } + +static void sha1_init(ops_hash_t *hash) + { + if (debug) + { + fprintf(stderr,"***\n***\nsha1_init\n***\n"); + } + assert(!hash->data); + hash->data=malloc(sizeof(SHA_CTX)); + SHA1_Init(hash->data); + } + +static void sha1_add(ops_hash_t *hash,const unsigned char *data, + unsigned length) + { + if (debug) + { + unsigned int i=0; + fprintf(stderr,"adding %d to hash:\n ", length); + for (i=0; idata,data,length); + } + +static unsigned sha1_finish(ops_hash_t *hash,unsigned char *out) + { + SHA1_Final(out,hash->data); + if (debug) + { + unsigned i=0; + fprintf(stderr,"***\n***\nsha1_finish\n***\n"); + for (i=0; idata); + hash->data=NULL; + return SHA_DIGEST_LENGTH; + } + +static ops_hash_t sha1={OPS_HASH_SHA1,SHA_DIGEST_LENGTH,"SHA1",sha1_init, + sha1_add,sha1_finish,NULL}; + +/** + \ingroup Core_Crypto + \brief Initialise to SHA1 + \param hash Hash to initialise +*/ +void ops_hash_sha1(ops_hash_t *hash) + { + *hash=sha1; + } + +static void sha256_init(ops_hash_t *hash) + { + if (debug) + { + fprintf(stderr,"***\n***\nsha256_init\n***\n"); + } + assert(!hash->data); + hash->data=malloc(sizeof(SHA256_CTX)); + SHA256_Init(hash->data); + } + +static void sha256_add(ops_hash_t *hash,const unsigned char *data, + unsigned length) + { + if (debug) + { + unsigned int i=0; + fprintf(stderr,"adding %d to hash:\n ", length); + for (i=0; idata,data,length); + } + +static unsigned sha256_finish(ops_hash_t *hash,unsigned char *out) + { + SHA256_Final(out,hash->data); + if (debug) + { + unsigned i=0; + fprintf(stderr,"***\n***\nsha1_finish\n***\n"); + for (i=0; idata); + hash->data=NULL; + return SHA256_DIGEST_LENGTH; + } + +static ops_hash_t sha256={OPS_HASH_SHA256,SHA256_DIGEST_LENGTH,"SHA256",sha256_init, + sha256_add,sha256_finish,NULL}; + +void ops_hash_sha256(ops_hash_t *hash) + { + *hash=sha256; + } + +/* + * SHA384 + */ + +static void sha384_init(ops_hash_t *hash) + { + if (debug) + { + fprintf(stderr,"***\n***\nsha384_init\n***\n"); + } + assert(!hash->data); + hash->data=malloc(sizeof(SHA512_CTX)); + SHA384_Init(hash->data); + } + +static void sha384_add(ops_hash_t *hash,const unsigned char *data, + unsigned length) + { + if (debug) + { + unsigned int i=0; + fprintf(stderr,"adding %d to hash:\n ", length); + for (i=0; idata,data,length); + } + +static unsigned sha384_finish(ops_hash_t *hash,unsigned char *out) + { + SHA384_Final(out,hash->data); + if (debug) + { + unsigned i=0; + fprintf(stderr,"***\n***\nsha1_finish\n***\n"); + for (i=0; idata); + hash->data=NULL; + return SHA384_DIGEST_LENGTH; + } + +static ops_hash_t sha384={OPS_HASH_SHA384,SHA384_DIGEST_LENGTH,"SHA384",sha384_init, + sha384_add,sha384_finish,NULL}; + +void ops_hash_sha384(ops_hash_t *hash) + { + *hash=sha384; + } + +/* + * SHA512 + */ + +static void sha512_init(ops_hash_t *hash) + { + if (debug) + { + fprintf(stderr,"***\n***\nsha512_init\n***\n"); + } + assert(!hash->data); + hash->data=malloc(sizeof(SHA512_CTX)); + SHA512_Init(hash->data); + } + +static void sha512_add(ops_hash_t *hash,const unsigned char *data, + unsigned length) + { + if (debug) + { + unsigned int i=0; + fprintf(stderr,"adding %d to hash:\n ", length); + for (i=0; idata,data,length); + } + +static unsigned sha512_finish(ops_hash_t *hash,unsigned char *out) + { + SHA512_Final(out,hash->data); + if (debug) + { + unsigned i=0; + fprintf(stderr,"***\n***\nsha1_finish\n***\n"); + for (i=0; idata); + hash->data=NULL; + return SHA512_DIGEST_LENGTH; + } + +static ops_hash_t sha512={OPS_HASH_SHA512,SHA512_DIGEST_LENGTH,"SHA512",sha512_init, + sha512_add,sha512_finish,NULL}; + +void ops_hash_sha512(ops_hash_t *hash) + { + *hash=sha512; + } + +/* + * SHA224 + */ + +static void sha224_init(ops_hash_t *hash) + { + if (debug) + { + fprintf(stderr,"***\n***\nsha1_init\n***\n"); + } + assert(!hash->data); + hash->data=malloc(sizeof(SHA256_CTX)); + SHA224_Init(hash->data); + } + +static void sha224_add(ops_hash_t *hash,const unsigned char *data, + unsigned length) + { + if (debug) + { + unsigned int i=0; + fprintf(stderr,"adding %d to hash:\n ", length); + for (i=0; idata,data,length); + } + +static unsigned sha224_finish(ops_hash_t *hash,unsigned char *out) + { + SHA224_Final(out,hash->data); + if (debug) + { + unsigned i=0; + fprintf(stderr,"***\n***\nsha1_finish\n***\n"); + for (i=0; idata); + hash->data=NULL; + return SHA224_DIGEST_LENGTH; + } + +static ops_hash_t sha224={OPS_HASH_SHA224,SHA224_DIGEST_LENGTH,"SHA224",sha224_init, + sha224_add,sha224_finish,NULL}; + +void ops_hash_sha224(ops_hash_t *hash) + { + *hash=sha224; + } + +ops_boolean_t ops_dsa_verify(const unsigned char *hash,size_t hash_length, + const ops_dsa_signature_t *sig, + const ops_dsa_public_key_t *dsa) + { + DSA_SIG *osig; + DSA *odsa; + int ret; + + osig=DSA_SIG_new(); + osig->r=sig->r; + osig->s=sig->s; + + odsa=DSA_new(); + odsa->p=dsa->p; + odsa->q=dsa->q; + odsa->g=dsa->g; + odsa->pub_key=dsa->y; + + if (debug) + { + fprintf(stderr,"hash passed in:\n"); + unsigned i; + for (i=0; iq)); + unsigned int qlen=BN_num_bytes(odsa->q); + if (qlen < hash_length) + hash_length=qlen; + // ret=DSA_do_verify(hash,hash_length,osig,odsa); + ret=DSA_do_verify(hash,hash_length,osig,odsa); + if (debug) + { + fprintf(stderr,"ret=%d\n",ret); + } + assert(ret >= 0); + + odsa->p=odsa->q=odsa->g=odsa->pub_key=NULL; + DSA_free(odsa); + + osig->r=osig->s=NULL; + DSA_SIG_free(osig); + + return ret != 0; + } + +/** + \ingroup Core_Crypto + \brief Recovers message digest from the signature + \param out Where to write decrypted data to + \param in Encrypted data + \param length Length of encrypted data + \param rsa RSA public key + \return size of recovered message digest +*/ +int ops_rsa_public_decrypt(unsigned char *out,const unsigned char *in, + size_t length,const ops_rsa_public_key_t *rsa) + { + RSA *orsa; + int n; + + orsa=RSA_new(); + orsa->n=rsa->n; + orsa->e=rsa->e; + + n=RSA_public_decrypt(length,in,out,orsa,RSA_NO_PADDING); + + orsa->n=orsa->e=NULL; + RSA_free(orsa); + + return n; + } + +/** + \ingroup Core_Crypto + \brief Signs data with RSA + \param out Where to write signature + \param in Data to sign + \param length Length of data + \param srsa RSA secret key + \param rsa RSA public key + \return number of bytes decrypted +*/ +int ops_rsa_private_encrypt(unsigned char *out,const unsigned char *in, + size_t length,const ops_rsa_secret_key_t *srsa, + const ops_rsa_public_key_t *rsa) + { + RSA *orsa; + int n; + + orsa=RSA_new(); + orsa->n=rsa->n; // XXX: do we need n? + orsa->d=srsa->d; + orsa->p=srsa->q; + orsa->q=srsa->p; + + /* debug */ + orsa->e=rsa->e; + // If this isn't set, it's very likely that the programmer hasn't + // decrypted the secret key. RSA_check_key segfaults in that case. + // Use ops_decrypt_secret_key_from_data() to do that. + assert(orsa->d); + assert(RSA_check_key(orsa) == 1); + orsa->e=NULL; + /* end debug */ + + n=RSA_private_encrypt(length,in,out,orsa,RSA_NO_PADDING); + + orsa->n=orsa->d=orsa->p=orsa->q=NULL; + RSA_free(orsa); + + return n; + } + +/** +\ingroup Core_Crypto +\brief Decrypts RSA-encrypted data +\param out Where to write the plaintext +\param in Encrypted data +\param length Length of encrypted data +\param srsa RSA secret key +\param rsa RSA public key +\return size of recovered plaintext +*/ +int ops_rsa_private_decrypt(unsigned char *out,const unsigned char *in, + size_t length,const ops_rsa_secret_key_t *srsa, + const ops_rsa_public_key_t *rsa) + { + RSA *orsa; + int n; + char errbuf[1024]; + + orsa=RSA_new(); + orsa->n=rsa->n; // XXX: do we need n? + orsa->d=srsa->d; + orsa->p=srsa->q; + orsa->q=srsa->p; + + /* debug */ + orsa->e=rsa->e; + assert(RSA_check_key(orsa) == 1); + orsa->e=NULL; + /* end debug */ + + n=RSA_private_decrypt(length,in,out,orsa,RSA_NO_PADDING); + + // printf("ops_rsa_private_decrypt: n=%d\n",n); + + errbuf[0]='\0'; + if (n==-1) + { + unsigned long err=ERR_get_error(); + ERR_error_string(err,&errbuf[0]); + fprintf(stderr,"openssl error : %s\n",errbuf); + } + orsa->n=orsa->d=orsa->p=orsa->q=NULL; + RSA_free(orsa); + + return n; + } + +/** + \ingroup Core_Crypto + \brief RSA-encrypts data + \param out Where to write the encrypted data + \param in Plaintext + \param length Size of plaintext + \param rsa RSA Public Key +*/ +int ops_rsa_public_encrypt(unsigned char *out,const unsigned char *in, + size_t length,const ops_rsa_public_key_t *rsa) + { + RSA *orsa; + int n; + + // printf("ops_rsa_public_encrypt: length=%ld\n", length); + + orsa=RSA_new(); + orsa->n=rsa->n; + orsa->e=rsa->e; + + // printf("len: %ld\n", length); + // ops_print_bn("n: ", orsa->n); + // ops_print_bn("e: ", orsa->e); + n=RSA_public_encrypt(length,in,out,orsa,RSA_NO_PADDING); + + if (n==-1) + { + BIO *fd_out; + fd_out=BIO_new_fd(fileno(stderr), BIO_NOCLOSE); + ERR_print_errors(fd_out); + } + + orsa->n=orsa->e=NULL; + RSA_free(orsa); + + return n; + } + +/** + \ingroup Core_Crypto + \brief initialises openssl + \note Would usually call ops_init() instead + \sa ops_init() +*/ +void ops_crypto_init() + { +#ifdef DMALLOC + CRYPTO_malloc_debug_init(); + CRYPTO_dbg_set_options(V_CRYPTO_MDEBUG_ALL); + CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON); +#endif + } + +/** + \ingroup Core_Crypto + \brief Finalise openssl + \note Would usually call ops_finish() instead + \sa ops_finish() +*/ +void ops_crypto_finish() + { + CRYPTO_cleanup_all_ex_data(); + ERR_remove_state(0); +#ifdef DMALLOC + CRYPTO_mem_leaks_fp(stderr); +#endif + } + +/** + \ingroup Core_Hashes + \brief Get Hash name + \param hash Hash struct + \return Hash name +*/ +const char *ops_text_from_hash(ops_hash_t *hash) + { return hash->name; } + +/** + \ingroup HighLevel_KeyGenerate + \brief Generates an RSA keypair + \param numbits Modulus size + \param e Public Exponent + \param keydata Pointer to keydata struct to hold new key + \return ops_true if key generated successfully; otherwise ops_false + \note It is the caller's responsibility to call ops_keydata_free(keydata) +*/ +ops_boolean_t ops_rsa_generate_keypair(const int numbits, const unsigned long e, ops_keydata_t* keydata) + { + ops_secret_key_t *skey=NULL; + RSA *rsa=NULL; + BN_CTX *ctx=BN_CTX_new(); + + ops_keydata_init(keydata,OPS_PTAG_CT_SECRET_KEY); + skey=ops_get_writable_secret_key_from_data(keydata); + + // generate the key pair + + rsa=RSA_generate_key(numbits,e,NULL,NULL); + + // populate ops key from ssl key + + skey->public_key.version=4; + skey->public_key.creation_time=time(NULL); + skey->public_key.days_valid=0; + skey->public_key.algorithm= OPS_PKA_RSA; + + skey->public_key.key.rsa.n=BN_dup(rsa->n); + skey->public_key.key.rsa.e=BN_dup(rsa->e); + + skey->s2k_usage=OPS_S2KU_ENCRYPTED_AND_HASHED; + skey->s2k_specifier=OPS_S2KS_SALTED; + //skey->s2k_specifier=OPS_S2KS_SIMPLE; + skey->algorithm=OPS_SA_CAST5; // \todo make param + skey->hash_algorithm=OPS_HASH_SHA1; // \todo make param + skey->octet_count=0; + skey->checksum=0; + + skey->key.rsa.d=BN_dup(rsa->d); + skey->key.rsa.p=BN_dup(rsa->p); + skey->key.rsa.q=BN_dup(rsa->q); + skey->key.rsa.u=BN_mod_inverse(NULL,rsa->p, rsa->q, ctx); + assert(skey->key.rsa.u); + BN_CTX_free(ctx); + + RSA_free(rsa); + + ops_keyid(keydata->key_id, &keydata->key.skey.public_key); + ops_fingerprint(&keydata->fingerprint, &keydata->key.skey.public_key); + + // Generate checksum + + ops_create_info_t *cinfo=NULL; + ops_memory_t *mem=NULL; + + ops_setup_memory_write(&cinfo, &mem, 128); + + ops_push_skey_checksum_writer(cinfo, skey); + + switch(skey->public_key.algorithm) + { + // case OPS_PKA_DSA: + // return ops_write_mpi(key->key.dsa.x,info); + + case OPS_PKA_RSA: + case OPS_PKA_RSA_ENCRYPT_ONLY: + case OPS_PKA_RSA_SIGN_ONLY: + if(!ops_write_mpi(skey->key.rsa.d,cinfo) + || !ops_write_mpi(skey->key.rsa.p,cinfo) + || !ops_write_mpi(skey->key.rsa.q,cinfo) + || !ops_write_mpi(skey->key.rsa.u,cinfo)) + return ops_false; + break; + + // case OPS_PKA_ELGAMAL: + // return ops_write_mpi(key->key.elgamal.x,info); + + default: + assert(0); + break; + } + + // close rather than pop, since its the only one on the stack + ops_writer_close(cinfo); + ops_teardown_memory_write(cinfo, mem); + + // should now have checksum in skey struct + + // test + if (debug) + test_secret_key(skey); + + return ops_true; + } + +/** + \ingroup HighLevel_KeyGenerate + \brief Creates a self-signed RSA keypair + \param numbits Modulus size + \param e Public Exponent + \param userid User ID + \return The new keypair or NULL + + \note It is the caller's responsibility to call ops_keydata_free(keydata) + \sa ops_rsa_generate_keypair() + \sa ops_keydata_free() +*/ +ops_keydata_t* ops_rsa_create_selfsigned_keypair(const int numbits, const unsigned long e, ops_user_id_t * userid) + { + ops_keydata_t *keydata=NULL; + + keydata=ops_keydata_new(); + + if (ops_rsa_generate_keypair(numbits, e, keydata) != ops_true + || ops_add_selfsigned_userid_to_keydata(keydata, userid) != ops_true) + { + ops_keydata_free(keydata); + return NULL; + } + + return keydata; + } + +/* +int ops_dsa_size(const ops_dsa_public_key_t *dsa) + { + int size; + DSA *odsa; + odsa=DSA_new(); + odsa->p=dsa->p; + odsa->q=dsa->q; + odsa->g=dsa->g; + odsa->pub_key=dsa->y; + + DSAparams_print_fp(stderr, odsa); + size=DSA_size(odsa); + + odsa->p=odsa->q=odsa->g=odsa->pub_key=odsa->priv_key=NULL; + DSA_free(odsa); + + return size; + } +*/ + +DSA_SIG* ops_dsa_sign(unsigned char* hashbuf, unsigned hashsize, const ops_dsa_secret_key_t *sdsa, const ops_dsa_public_key_t *dsa) + { + DSA *odsa; + DSA_SIG *dsasig; + + odsa=DSA_new(); + odsa->p=dsa->p; + odsa->q=dsa->q; + odsa->g=dsa->g; + odsa->pub_key=dsa->y; + odsa->priv_key=sdsa->x; + + dsasig=DSA_do_sign(hashbuf,hashsize,odsa); + + odsa->p=odsa->q=odsa->g=odsa->pub_key=odsa->priv_key=NULL; + DSA_free(odsa); + + return dsasig; + } + +// eof diff --git a/openpgpsdk/src/packet-parse.c b/openpgpsdk/src/packet-parse.c new file mode 100644 index 000000000..8fba99e83 --- /dev/null +++ b/openpgpsdk/src/packet-parse.c @@ -0,0 +1,3261 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + * \brief Parser for OpenPGP packets + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "parse_local.h" + +#include +#include +#include +#include +#ifndef WIN32 +#include +#endif +#include +#include + +#include + +static int debug=0; + +/** + * limited_read_data reads the specified amount of the subregion's data + * into a data_t structure + * + * \param data Empty structure which will be filled with data + * \param len Number of octets to read + * \param subregion + * \param pinfo How to parse + * + * \return 1 on success, 0 on failure + */ +static int limited_read_data(ops_data_t *data,unsigned int len, + ops_region_t *subregion,ops_parse_info_t *pinfo) + { + data->len = len; + + assert(subregion->length-subregion->length_read >= len); + + data->contents=malloc(data->len); + if (!data->contents) + return 0; + + if (!ops_limited_read(data->contents, data->len,subregion,&pinfo->errors, + &pinfo->rinfo,&pinfo->cbinfo)) + return 0; + + return 1; + } + +/** + * read_data reads the remainder of the subregion's data + * into a data_t structure + * + * \param data + * \param subregion + * \param pinfo + * + * \return 1 on success, 0 on failure + */ +static int read_data(ops_data_t *data,ops_region_t *subregion, + ops_parse_info_t *pinfo) + { + int len; + + len=subregion->length-subregion->length_read; + + if ( len >= 0 ) { + return(limited_read_data(data,len,subregion,pinfo)); + } + return 0; + } + +/** + * Reads the remainder of the subregion as a string. + * It is the user's responsibility to free the memory allocated here. + */ + +static int read_unsigned_string(unsigned char **str,ops_region_t *subregion, + ops_parse_info_t *pinfo) + { + int len=0; + + len=subregion->length-subregion->length_read; + + *str=malloc(len+1); + if(!(*str)) + return 0; + + if(len && !ops_limited_read(*str,len,subregion,&pinfo->errors, + &pinfo->rinfo,&pinfo->cbinfo)) + return 0; + + /*! ensure the string is NULL-terminated */ + + (*str)[len]='\0'; + + return 1; + } + +static int read_string(char **str, ops_region_t *subregion, ops_parse_info_t *pinfo) + { + return (read_unsigned_string((unsigned char **)str, subregion, pinfo)); + } + +void ops_init_subregion(ops_region_t *subregion,ops_region_t *region) + { + memset(subregion,'\0',sizeof *subregion); + subregion->parent=region; + } + +/*! macro to save typing */ +#define C content.content + +/* XXX: replace ops_ptag_t with something more appropriate for limiting + reads */ + +/** + * low-level function to read data from reader function + * + * Use this function, rather than calling the reader directly. + * + * If the accumulate flag is set in *pinfo, the function + * adds the read data to the accumulated data, and updates + * the accumulated length. This is useful if, for example, + * the application wants access to the raw data as well as the + * parsed data. + * + * This function will also try to read the entire amount asked for, but not + * if it is over INT_MAX. Obviously many callers will know that they + * never ask for that much and so can avoid the extra complexity of + * dealing with return codes and filled-in lengths. + * + * \param *dest + * \param *plength + * \param flags + * \param *pinfo + * + * \return OPS_R_OK + * \return OPS_R_PARTIAL_READ + * \return OPS_R_EOF + * \return OPS_R_EARLY_EOF + * + * \sa #ops_reader_ret_t for details of return codes + */ + +static int sub_base_read(void *dest,size_t length,ops_error_t **errors, + ops_reader_info_t *rinfo,ops_parse_cb_info_t *cbinfo) + { + size_t n; + + /* reading more than this would look like an error */ + if(length > INT_MAX) + length=INT_MAX; + + for(n=0 ; n < length ; ) + { + int r=rinfo->reader((char*)dest+n,length-n,errors,rinfo,cbinfo); + + assert(r <= (int)(length-n)); + + // XXX: should we save the error and return what was read so far? + if(r < 0) + return r; + + if(r == 0) + break; + + n+=r; + } + + if(n == 0) + return 0; + + if(rinfo->accumulate) + { + assert(rinfo->asize >= rinfo->alength); + if(rinfo->alength+n > rinfo->asize) + { + rinfo->asize=rinfo->asize*2+n; + rinfo->accumulated=realloc(rinfo->accumulated,rinfo->asize); + } + assert(rinfo->asize >= rinfo->alength+n); + memcpy(rinfo->accumulated+rinfo->alength,dest,n); + } + // we track length anyway, because it is used for packet offsets + rinfo->alength+=n; + // and also the position + rinfo->position+=n; + + return n; + } + +int ops_stacked_read(void *dest,size_t length,ops_error_t **errors, + ops_reader_info_t *rinfo,ops_parse_cb_info_t *cbinfo) + { return sub_base_read(dest,length,errors,rinfo->next,cbinfo); } + +/* This will do a full read so long as length < MAX_INT */ +static int base_read(unsigned char *dest,size_t length, + ops_parse_info_t *pinfo) + { + return sub_base_read(dest,length,&pinfo->errors,&pinfo->rinfo, + &pinfo->cbinfo); + } + +/* Read a full size_t's worth. If the return is < than length, then + * *last_read tells you why - < 0 for an error, == 0 for EOF */ + +static size_t full_read(unsigned char *dest,size_t length,int *last_read, + ops_error_t **errors,ops_reader_info_t *rinfo, + ops_parse_cb_info_t *cbinfo) + { + size_t t; + int r=0; /* preset in case some loon calls with length == 0 */ + + for(t=0 ; t < length ; ) + { + r=sub_base_read(dest+t,length-t,errors,rinfo,cbinfo); + + if(r <= 0) + { + *last_read=r; + return t; + } + + t+=r; + } + + *last_read=r; + + return t; + } + + + +/** Read a scalar value of selected length from reader. + * + * Read an unsigned scalar value from reader in Big Endian representation. + * + * This function does not know or care about packet boundaries. It + * also assumes that an EOF is an error. + * + * \param *result The scalar value is stored here + * \param *reader Our reader + * \param length How many bytes to read + * \return ops_true on success, ops_false on failure + */ +static ops_boolean_t _read_scalar(unsigned *result,unsigned length, + ops_parse_info_t *pinfo) + { + unsigned t=0; + + assert (length <= sizeof(*result)); + + while(length--) + { + unsigned char c[1]; + int r; + + r=base_read(c,1,pinfo); + if(r != 1) + return ops_false; + t=(t << 8)+c[0]; + } + + *result=t; + return ops_true; + } + +/** + * \ingroup Core_ReadPackets + * \brief Read bytes from a region within the packet. + * + * Read length bytes into the buffer pointed to by *dest. + * Make sure we do not read over the packet boundary. + * Updates the Packet Tag's ops_ptag_t::length_read. + * + * If length would make us read over the packet boundary, or if + * reading fails, we call the callback with an error. + * + * Note that if the region is indeterminate, this can return a short + * read - check region->last_read for the length. EOF is indicated by + * a success return and region->last_read == 0 in this case (for a + * region of known length, EOF is an error). + * + * This function makes sure to respect packet boundaries. + * + * \param dest The destination buffer + * \param length How many bytes to read + * \param region Pointer to packet region + * \param errors Error stack + * \param rinfo Reader info + * \param cbinfo Callback info + * \return ops_true on success, ops_false on error + */ +ops_boolean_t ops_limited_read(unsigned char *dest,size_t length, + ops_region_t *region,ops_error_t **errors, + ops_reader_info_t *rinfo, + ops_parse_cb_info_t *cbinfo) + { + size_t r; + int lr; + + if(!region->indeterminate && region->length_read+length > region->length) + { + OPS_ERROR(errors,OPS_E_P_NOT_ENOUGH_DATA,"Not enough data"); + return ops_false; + } + + r=full_read(dest,length,&lr,errors,rinfo,cbinfo); + + if(lr < 0) + { + OPS_ERROR(errors,OPS_E_R_READ_FAILED,"Read failed"); + return ops_false; + } + + if(!region->indeterminate && r != length) + { + OPS_ERROR(errors,OPS_E_R_READ_FAILED,"Read failed"); + return ops_false; + } + + region->last_read=r; + do + { + region->length_read+=r; + assert(!region->parent || region->length <= region->parent->length); + } + while((region=region->parent)); + + return ops_true; + } + +/** + \ingroup Core_ReadPackets + \brief Call ops_limited_read on next in stack +*/ +ops_boolean_t ops_stacked_limited_read(unsigned char *dest,unsigned length, + ops_region_t *region, + ops_error_t **errors, + ops_reader_info_t *rinfo, + ops_parse_cb_info_t *cbinfo) + { return ops_limited_read(dest,length,region,errors,rinfo->next,cbinfo); } + +static ops_boolean_t limited_read(unsigned char *dest,unsigned length, + ops_region_t *region,ops_parse_info_t *info) + { + return ops_limited_read(dest,length,region,&info->errors, + &info->rinfo,&info->cbinfo); + } + +static ops_boolean_t exact_limited_read(unsigned char *dest,unsigned length, + ops_region_t *region, + ops_parse_info_t *pinfo) + { + ops_boolean_t ret; + + pinfo->exact_read=ops_true; + ret=limited_read(dest,length,region,pinfo); + pinfo->exact_read=ops_false; + + return ret; + } + +/** Skip over length bytes of this packet. + * + * Calls limited_read() to skip over some data. + * + * This function makes sure to respect packet boundaries. + * + * \param length How many bytes to skip + * \param *region Pointer to packet region + * \param *pinfo How to parse + * \return 1 on success, 0 on error (calls the cb with OPS_PARSER_ERROR in limited_read()). + */ +static int limited_skip(unsigned length,ops_region_t *region, + ops_parse_info_t *pinfo) + { + unsigned char buf[8192]; + + while(length) + { + int n=length%8192; + if(!limited_read(buf,n,region,pinfo)) + return 0; + length-=n; + } + return 1; + } + +/** Read a scalar. + * + * Read a big-endian scalar of length bytes, respecting packet + * boundaries (by calling limited_read() to read the raw data). + * + * This function makes sure to respect packet boundaries. + * + * \param *dest The scalar value is stored here + * \param length How many bytes make up this scalar (at most 4) + * \param *region Pointer to current packet region + * \param *pinfo How to parse + * \param *cb The callback + * \return 1 on success, 0 on error (calls the cb with OPS_PARSER_ERROR in limited_read()). + * + * \see RFC4880 3.1 + */ +static int limited_read_scalar(unsigned *dest,unsigned length, + ops_region_t *region, + ops_parse_info_t *pinfo) + { + unsigned char c[4]=""; + unsigned t; + unsigned n; + + assert(length <= 4); + assert(sizeof(*dest) >= 4); + if(!limited_read(c,length,region,pinfo)) + return 0; + + for(t=0,n=0 ; n < length ; ++n) + t=(t << 8)+c[n]; + *dest=t; + + return 1; + } + +/** Read a scalar. + * + * Read a big-endian scalar of length bytes, respecting packet + * boundaries (by calling limited_read() to read the raw data). + * + * The value read is stored in a size_t, which is a different size + * from an unsigned on some platforms. + * + * This function makes sure to respect packet boundaries. + * + * \param *dest The scalar value is stored here + * \param length How many bytes make up this scalar (at most 4) + * \param *region Pointer to current packet region + * \param *pinfo How to parse + * \param *cb The callback + * \return 1 on success, 0 on error (calls the cb with OPS_PARSER_ERROR in limited_read()). + * + * \see RFC4880 3.1 + */ +static int limited_read_size_t_scalar(size_t *dest,unsigned length, + ops_region_t *region, + ops_parse_info_t *pinfo) + { + unsigned tmp; + + assert(sizeof(*dest) >= 4); + + /* Note that because the scalar is at most 4 bytes, we don't care + if size_t is bigger than usigned */ + if(!limited_read_scalar(&tmp,length,region,pinfo)) + return 0; + + *dest=tmp; + return 1; + } + +/** Read a timestamp. + * + * Timestamps in OpenPGP are unix time, i.e. seconds since The Epoch (1.1.1970). They are stored in an unsigned scalar + * of 4 bytes. + * + * This function reads the timestamp using limited_read_scalar(). + * + * This function makes sure to respect packet boundaries. + * + * \param *dest The timestamp is stored here + * \param *ptag Pointer to current packet's Packet Tag. + * \param *reader Our reader + * \param *cb The callback + * \return see limited_read_scalar() + * + * \see RFC4880 3.5 + */ +static int limited_read_time(time_t *dest,ops_region_t *region, + ops_parse_info_t *pinfo) + { + /* + * Cannot assume that time_t is 4 octets long - + * there is at least one architecture (SunOS 5.10) where it is 8. + */ + if (sizeof(*dest)==4) + { + return limited_read_scalar((unsigned *)dest,4,region,pinfo); + } + else + { + time_t mytime=0; + int i=0; + unsigned char c[1]; + for (i=0; i<4; i++) + { + if (!limited_read(c,1,region,pinfo)) + return 0; + mytime=(mytime << 8) + c[0]; + } + *dest=mytime; + return 1; + } + } + +/** + * \ingroup Core_MPI + * Read a multiprecision integer. + * + * Large numbers (multiprecision integers, MPI) are stored in OpenPGP in two parts. First there is a 2 byte scalar + * indicating the length of the following MPI in Bits. Then follow the bits that make up the actual number, most + * significant bits first (Big Endian). The most significant bit in the MPI is supposed to be 1 (unless the MPI is + * encrypted - then it may be different as the bit count refers to the plain text but the bits are encrypted). + * + * Unused bits (i.e. those filling up the most significant byte from the left to the first bits that counts) are + * supposed to be cleared - I guess. XXX - does anything actually say so? + * + * This function makes sure to respect packet boundaries. + * + * \param **pgn return the integer there - the BIGNUM is created by BN_bin2bn() and probably needs to be freed + * by the caller XXX right ben? + * \param *ptag Pointer to current packet's Packet Tag. + * \param *reader Our reader + * \param *cb The callback + * \return 1 on success, 0 on error (by limited_read_scalar() or limited_read() or if the MPI is not properly formed (XXX + * see comment below - the callback is called with a OPS_PARSER_ERROR in case of an error) + * + * \see RFC4880 3.2 + */ +static int limited_read_mpi(BIGNUM **pbn,ops_region_t *region, + ops_parse_info_t *pinfo) + { + unsigned length; + unsigned nonzero; + unsigned char buf[8192]=""; /* an MPI has a 2 byte length part. Length + is given in bits, so the largest we should + ever need for the buffer is 8192 bytes. */ + ops_boolean_t ret; + + pinfo->reading_mpi_length=ops_true; + ret=limited_read_scalar(&length,2,region,pinfo); + + pinfo->reading_mpi_length=ops_false; + if(!ret) + return 0; + + nonzero=length&7; /* there should be this many zero bits in the MS byte */ + if(!nonzero) + nonzero=8; + length=(length+7)/8; + + assert(length <= 8192); + if(!limited_read(buf,length,region,pinfo)) + return 0; + + if((buf[0] >> nonzero) != 0 || !(buf[0]&(1 << (nonzero-1)))) + { + OPS_ERROR(&pinfo->errors,OPS_E_P_MPI_FORMAT_ERROR,"MPI Format error"); /* XXX: Ben, one part of this constraint does not apply to encrypted MPIs the draft says. -- peter */ + return 0; + } + + *pbn=BN_bin2bn(buf,length,NULL); + return 1; + } + +/** Read some data with a New-Format length from reader. + * + * \sa Internet-Draft RFC4880.txt Section 4.2.2 + * + * \param *length Where the decoded length will be put + * \param *pinfo How to parse + * \return ops_true if OK, else ops_false + * + */ + +static ops_boolean_t read_new_length(unsigned *length,ops_parse_info_t *pinfo) + { + unsigned char c[1]; + + if(base_read(c,1,pinfo) != 1) + return ops_false; + if(c[0] < 192) + { + // 1. One-octet packet + *length=c[0]; + return ops_true; + } + + else if (c[0]>=192 && c[0]<=223) + { + // 2. Two-octet packet + unsigned t=(c[0]-192) << 8; + + if(base_read(c,1,pinfo) != 1) + return ops_false; + *length=t+c[0]+192; + return ops_true; + } + + else if (c[0]==255) + { + // 3. Five-Octet packet + return _read_scalar(length,4,pinfo); + } + + else if (c[0]>=224 && c[0]<255) + { + // 4. Partial Body Length + OPS_ERROR(&pinfo->errors,OPS_E_UNIMPLEMENTED, + "New format Partial Body Length fields not yet implemented"); + return ops_false; + } + return ops_false; + } + +/** Read the length information for a new format Packet Tag. + * + * New style Packet Tags encode the length in one to five octets. This function reads the right amount of bytes and + * decodes it to the proper length information. + * + * This function makes sure to respect packet boundaries. + * + * \param *length return the length here + * \param *ptag Pointer to current packet's Packet Tag. + * \param *reader Our reader + * \param *cb The callback + * \return 1 on success, 0 on error (by limited_read_scalar() or limited_read() or if the MPI is not properly formed (XXX + * see comment below) + * + * \see RFC4880 4.2.2 + * \see ops_ptag_t + */ +static int limited_read_new_length(unsigned *length,ops_region_t *region, + ops_parse_info_t *pinfo) + { + unsigned char c[1]=""; + + if(!limited_read(c,1,region,pinfo)) + return 0; + if(c[0] < 192) + { + *length=c[0]; + return 1; + } + if(c[0] < 255) + { + unsigned t=(c[0]-192) << 8; + + if(!limited_read(c,1,region,pinfo)) + return 0; + *length=t+c[0]+192; + return 1; + } + return limited_read_scalar(length,4,region,pinfo); + } + +/** +\ingroup Core_Create +\brief Free allocated memory +*/ +static void data_free(ops_data_t *data) + { + free(data->contents); + data->contents=NULL; + data->len=0; + } + +/** +\ingroup Core_Create +\brief Free allocated memory +*/ +static void string_free(char **str) + { + free(*str); + *str=NULL; + } + +/** +\ingroup Core_Create +\brief Free allocated memory +*/ +/*! Free packet memory, set pointer to NULL */ +void ops_packet_free(ops_packet_t *packet) + { + free(packet->raw); + packet->raw=NULL; + } + +/** +\ingroup Core_Create +\brief Free allocated memory +*/ +void ops_headers_free(ops_headers_t *headers) + { + unsigned n; + + for(n=0 ; n < headers->nheaders ; ++n) + { + free(headers->headers[n].key); + free(headers->headers[n].value); + } + free(headers->headers); + headers->headers=NULL; + } + +/** +\ingroup Core_Create +\brief Free allocated memory +*/ +void ops_signed_cleartext_trailer_free(ops_signed_cleartext_trailer_t *trailer) + { + free(trailer->hash); + trailer->hash=NULL; + } + +/** +\ingroup Core_Create +\brief Free allocated memory +*/ +void ops_cmd_get_passphrase_free(ops_secret_key_passphrase_t *skp) + { + if (skp->passphrase && *skp->passphrase) + { + free(*skp->passphrase); + *skp->passphrase=NULL; + } + } + +/** +\ingroup Core_Create +\brief Free allocated memory +*/ +/*! Free any memory allocated when parsing the packet content */ +void ops_parser_content_free(ops_parser_content_t *c) + { + switch(c->tag) + { + case OPS_PARSER_PTAG: + case OPS_PTAG_CT_COMPRESSED: + case OPS_PTAG_SS_CREATION_TIME: + case OPS_PTAG_SS_EXPIRATION_TIME: + case OPS_PTAG_SS_KEY_EXPIRATION_TIME: + case OPS_PTAG_SS_TRUST: + case OPS_PTAG_SS_ISSUER_KEY_ID: + case OPS_PTAG_CT_ONE_PASS_SIGNATURE: + case OPS_PTAG_SS_PRIMARY_USER_ID: + case OPS_PTAG_SS_REVOCABLE: + case OPS_PTAG_SS_REVOCATION_KEY: + case OPS_PTAG_CT_LITERAL_DATA_HEADER: + case OPS_PTAG_CT_LITERAL_DATA_BODY: + case OPS_PTAG_CT_SIGNED_CLEARTEXT_BODY: + case OPS_PTAG_CT_UNARMOURED_TEXT: + case OPS_PTAG_CT_ARMOUR_TRAILER: + case OPS_PTAG_CT_SIGNATURE_HEADER: + case OPS_PTAG_CT_SE_DATA_HEADER: + case OPS_PTAG_CT_SE_IP_DATA_HEADER: + case OPS_PTAG_CT_SE_IP_DATA_BODY: + case OPS_PTAG_CT_MDC: + case OPS_PARSER_CMD_GET_SECRET_KEY: + break; + + case OPS_PTAG_CT_SIGNED_CLEARTEXT_HEADER: + ops_headers_free(&c->content.signed_cleartext_header.headers); + break; + + case OPS_PTAG_CT_ARMOUR_HEADER: + ops_headers_free(&c->content.armour_header.headers); + break; + + case OPS_PTAG_CT_SIGNED_CLEARTEXT_TRAILER: + ops_signed_cleartext_trailer_free(&c->content.signed_cleartext_trailer); + break; + + case OPS_PTAG_CT_TRUST: + ops_trust_free(&c->content.trust); + break; + + case OPS_PTAG_CT_SIGNATURE: + case OPS_PTAG_CT_SIGNATURE_FOOTER: + ops_signature_free(&c->content.signature); + break; + + case OPS_PTAG_CT_PUBLIC_KEY: + case OPS_PTAG_CT_PUBLIC_SUBKEY: + ops_public_key_free(&c->content.public_key); + break; + + case OPS_PTAG_CT_USER_ID: + ops_user_id_free(&c->content.user_id); + break; + + case OPS_PTAG_SS_SIGNERS_USER_ID: + ops_user_id_free(&c->content.ss_signers_user_id); + break; + + case OPS_PTAG_CT_USER_ATTRIBUTE: + ops_user_attribute_free(&c->content.user_attribute); + break; + + case OPS_PTAG_SS_PREFERRED_SKA: + ops_ss_preferred_ska_free(&c->content.ss_preferred_ska); + break; + + case OPS_PTAG_SS_PREFERRED_HASH: + ops_ss_preferred_hash_free(&c->content.ss_preferred_hash); + break; + + case OPS_PTAG_SS_PREFERRED_COMPRESSION: + ops_ss_preferred_compression_free(&c->content.ss_preferred_compression); + break; + + case OPS_PTAG_SS_KEY_FLAGS: + ops_ss_key_flags_free(&c->content.ss_key_flags); + break; + + case OPS_PTAG_SS_KEY_SERVER_PREFS: + ops_ss_key_server_prefs_free(&c->content.ss_key_server_prefs); + break; + + case OPS_PTAG_SS_FEATURES: + ops_ss_features_free(&c->content.ss_features); + break; + + case OPS_PTAG_SS_NOTATION_DATA: + ops_ss_notation_data_free(&c->content.ss_notation_data); + break; + + case OPS_PTAG_SS_REGEXP: + ops_ss_regexp_free(&c->content.ss_regexp); + break; + + case OPS_PTAG_SS_POLICY_URI: + ops_ss_policy_url_free(&c->content.ss_policy_url); + break; + + case OPS_PTAG_SS_PREFERRED_KEY_SERVER: + ops_ss_preferred_key_server_free(&c->content.ss_preferred_key_server); + break; + + case OPS_PTAG_SS_USERDEFINED00: + case OPS_PTAG_SS_USERDEFINED01: + case OPS_PTAG_SS_USERDEFINED02: + case OPS_PTAG_SS_USERDEFINED03: + case OPS_PTAG_SS_USERDEFINED04: + case OPS_PTAG_SS_USERDEFINED05: + case OPS_PTAG_SS_USERDEFINED06: + case OPS_PTAG_SS_USERDEFINED07: + case OPS_PTAG_SS_USERDEFINED08: + case OPS_PTAG_SS_USERDEFINED09: + case OPS_PTAG_SS_USERDEFINED10: + ops_ss_userdefined_free(&c->content.ss_userdefined); + break; + + case OPS_PTAG_SS_RESERVED: + ops_ss_reserved_free(&c->content.ss_unknown); + break; + + case OPS_PTAG_SS_REVOCATION_REASON: + ops_ss_revocation_reason_free(&c->content.ss_revocation_reason); + break; + + case OPS_PTAG_SS_EMBEDDED_SIGNATURE: + ops_ss_embedded_signature_free(&c->content.ss_embedded_signature); + break; + + case OPS_PARSER_PACKET_END: + ops_packet_free(&c->content.packet); + break; + + case OPS_PARSER_ERROR: + case OPS_PARSER_ERRCODE: + break; + + case OPS_PTAG_CT_SECRET_KEY: + case OPS_PTAG_CT_ENCRYPTED_SECRET_KEY: + ops_secret_key_free(&c->content.secret_key); + break; + + case OPS_PTAG_CT_PK_SESSION_KEY: + case OPS_PTAG_CT_ENCRYPTED_PK_SESSION_KEY: + ops_pk_session_key_free(&c->content.pk_session_key); + break; + + case OPS_PARSER_CMD_GET_SK_PASSPHRASE: + ops_cmd_get_passphrase_free(&c->content.secret_key_passphrase); + break; + + default: + fprintf(stderr,"Can't free %d (0x%x)\n",c->tag,c->tag); + assert(0); + } + } + +/** +\ingroup Core_Create +\brief Free allocated memory +*/ +static void free_BN(BIGNUM **pp) + { + BN_free(*pp); + *pp=NULL; + } + +/** +\ingroup Core_Create +\brief Free allocated memory +*/ +void ops_pk_session_key_free(ops_pk_session_key_t *sk) + { + switch(sk->algorithm) + { + case OPS_PKA_RSA: + free_BN(&sk->parameters.rsa.encrypted_m); + break; + + case OPS_PKA_ELGAMAL: + free_BN(&sk->parameters.elgamal.g_to_k); + free_BN(&sk->parameters.elgamal.encrypted_m); + break; + + default: + assert(0); + } + } + +/** +\ingroup Core_Create +\brief Free allocated memory +*/ +/*! Free the memory used when parsing a public key */ +void ops_public_key_free(ops_public_key_t *p) + { + switch(p->algorithm) + { + case OPS_PKA_RSA: + case OPS_PKA_RSA_ENCRYPT_ONLY: + case OPS_PKA_RSA_SIGN_ONLY: + free_BN(&p->key.rsa.n); + free_BN(&p->key.rsa.e); + break; + + case OPS_PKA_DSA: + free_BN(&p->key.dsa.p); + free_BN(&p->key.dsa.q); + free_BN(&p->key.dsa.g); + free_BN(&p->key.dsa.y); + break; + + case OPS_PKA_ELGAMAL: + case OPS_PKA_ELGAMAL_ENCRYPT_OR_SIGN: + free_BN(&p->key.elgamal.p); + free_BN(&p->key.elgamal.g); + free_BN(&p->key.elgamal.y); + break; + + //case 0: + // nothing to free + // break; + + default: + assert(0); + } + } + +/** + \ingroup Core_ReadPackets +*/ +static int parse_public_key_data(ops_public_key_t *key,ops_region_t *region, + ops_parse_info_t *pinfo) + { + unsigned char c[1]=""; + + assert (region->length_read == 0); /* We should not have read anything so far */ + + if(!limited_read(c,1,region,pinfo)) + return 0; + key->version=c[0]; + if(key->version < 2 || key->version > 4) + { + OPS_ERROR_1(&pinfo->errors,OPS_E_PROTO_BAD_PUBLIC_KEY_VRSN, + "Bad public key version (0x%02x)",key->version); + return 0; + } + + if(!limited_read_time(&key->creation_time,region,pinfo)) + return 0; + + key->days_valid=0; + if((key->version == 2 || key->version == 3) + && !limited_read_scalar(&key->days_valid,2,region,pinfo)) + return 0; + + if(!limited_read(c,1,region,pinfo)) + return 0; + + key->algorithm=c[0]; + + switch(key->algorithm) + { + case OPS_PKA_DSA: + if(!limited_read_mpi(&key->key.dsa.p,region,pinfo) + || !limited_read_mpi(&key->key.dsa.q,region,pinfo) + || !limited_read_mpi(&key->key.dsa.g,region,pinfo) + || !limited_read_mpi(&key->key.dsa.y,region,pinfo)) + return 0; + break; + + case OPS_PKA_RSA: + case OPS_PKA_RSA_ENCRYPT_ONLY: + case OPS_PKA_RSA_SIGN_ONLY: + if(!limited_read_mpi(&key->key.rsa.n,region,pinfo) + || !limited_read_mpi(&key->key.rsa.e,region,pinfo)) + return 0; + break; + + case OPS_PKA_ELGAMAL: + case OPS_PKA_ELGAMAL_ENCRYPT_OR_SIGN: + if(!limited_read_mpi(&key->key.elgamal.p,region,pinfo) + || !limited_read_mpi(&key->key.elgamal.g,region,pinfo) + || !limited_read_mpi(&key->key.elgamal.y,region,pinfo)) + return 0; + break; + + default: + OPS_ERROR_1(&pinfo->errors,OPS_E_ALG_UNSUPPORTED_PUBLIC_KEY_ALG,"Unsupported Public Key algorithm (%s)",ops_show_pka(key->algorithm)); + return 0; + } + + return 1; + } + + +/** + * \ingroup Core_ReadPackets + * \brief Parse a public key packet. + * + * This function parses an entire v3 (== v2) or v4 public key packet for RSA, ElGamal, and DSA keys. + * + * Once the key has been parsed successfully, it is passed to the callback. + * + * \param *ptag Pointer to the current Packet Tag. This function should consume the entire packet. + * \param *reader Our reader + * \param *cb The callback + * \return 1 on success, 0 on error + * + * \see RFC4880 5.5.2 + */ +static int parse_public_key(ops_content_tag_t tag,ops_region_t *region, + ops_parse_info_t *pinfo) + { + ops_parser_content_t content; + + if(!parse_public_key_data(&C.public_key,region,pinfo)) + return 0; + + // XXX: this test should be done for all packets, surely? + if(region->length_read != region->length) + { + OPS_ERROR_1(&pinfo->errors,OPS_E_R_UNCONSUMED_DATA, + "Unconsumed data (%d)", region->length-region->length_read); + return 0; + } + + CBP(pinfo,tag,&content); + + return 1; + } + + +/** +\ingroup Core_Create +\brief Free allocated memory +*/ +/*! Free the memory used when parsing this signature sub-packet type */ +void ops_ss_regexp_free(ops_ss_regexp_t *regexp) + { + string_free(®exp->text); + } + +/** +\ingroup Core_Create +\brief Free allocated memory +*/ +/*! Free the memory used when parsing this signature sub-packet type */ +void ops_ss_policy_url_free(ops_ss_policy_url_t *policy_url) + { + string_free(&policy_url->text); + } + +/** +\ingroup Core_Create +\brief Free allocated memory +*/ +/*! Free the memory used when parsing this signature sub-packet type */ +void ops_ss_preferred_key_server_free(ops_ss_preferred_key_server_t *preferred_key_server) + { + string_free(&preferred_key_server->text); + } + +/** +\ingroup Core_Create +\brief Free allocated memory +*/ +/*! Free the memory used when parsing this packet type */ +void ops_user_attribute_free(ops_user_attribute_t *user_att) + { + data_free(&user_att->data); + } + +/** + * \ingroup Core_ReadPackets + * \brief Parse one user attribute packet. + * + * User attribute packets contain one or more attribute subpackets. + * For now, handle the whole packet as raw data. + */ + +static int parse_user_attribute(ops_region_t *region, ops_parse_info_t *pinfo) + { + + ops_parser_content_t content; + + /* xxx- treat as raw data for now. Could break down further + into attribute sub-packets later - rachel */ + + assert(region->length_read == 0); /* We should not have read anything so far */ + + if(!read_data(&C.user_attribute.data,region,pinfo)) + return 0; + + CBP(pinfo,OPS_PTAG_CT_USER_ATTRIBUTE,&content); + + return 1; + } + +/** +\ingroup Core_Create +\brief Free allocated memory +*/ +/*! Free the memory used when parsing this packet type */ +void ops_user_id_free(ops_user_id_t *id) + { + free(id->user_id); + id->user_id=NULL; + } + +/** + * \ingroup Core_ReadPackets + * \brief Parse a user id. + * + * This function parses an user id packet, which is basically just a char array the size of the packet. + * + * The char array is to be treated as an UTF-8 string. + * + * The userid gets null terminated by this function. Freeing it is the responsibility of the caller. + * + * Once the userid has been parsed successfully, it is passed to the callback. + * + * \param *ptag Pointer to the Packet Tag. This function should consume the entire packet. + * \param *reader Our reader + * \param *cb The callback + * \return 1 on success, 0 on error + * + * \see RFC4880 5.11 + */ +static int parse_user_id(ops_region_t *region,ops_parse_info_t *pinfo) + { + ops_parser_content_t content; + + assert(region->length_read == 0); /* We should not have read anything so far */ + + C.user_id.user_id=malloc(region->length+1); /* XXX should we not like check malloc's return value? */ + + if(region->length && !limited_read(C.user_id.user_id,region->length,region, + pinfo)) + return 0; + + C.user_id.user_id[region->length]='\0'; /* terminate the string */ + + CBP(pinfo,OPS_PTAG_CT_USER_ID,&content); + + return 1; + } + +/** + * \ingroup Core_Create + * \brief Free the memory used when parsing a private/experimental PKA signature + * \param unknown_sig + */ +void free_unknown_sig_pka(ops_unknown_signature_t *unknown_sig) + { + data_free(&unknown_sig->data); + } + +/** + * \ingroup Core_Create + * \brief Free the memory used when parsing a signature + * \param sig + */ +void ops_signature_free(ops_signature_t *sig) + { + switch(sig->info.key_algorithm) + { + case OPS_PKA_RSA: + case OPS_PKA_RSA_SIGN_ONLY: + free_BN(&sig->info.signature.rsa.sig); + break; + + case OPS_PKA_DSA: + free_BN(&sig->info.signature.dsa.r); + free_BN(&sig->info.signature.dsa.s); + break; + + case OPS_PKA_ELGAMAL_ENCRYPT_OR_SIGN: + free_BN(&sig->info.signature.elgamal.r); + free_BN(&sig->info.signature.elgamal.s); + break; + + case OPS_PKA_PRIVATE00: + case OPS_PKA_PRIVATE01: + case OPS_PKA_PRIVATE02: + case OPS_PKA_PRIVATE03: + case OPS_PKA_PRIVATE04: + case OPS_PKA_PRIVATE05: + case OPS_PKA_PRIVATE06: + case OPS_PKA_PRIVATE07: + case OPS_PKA_PRIVATE08: + case OPS_PKA_PRIVATE09: + case OPS_PKA_PRIVATE10: + free_unknown_sig_pka(&sig->info.signature.unknown); + break; + + default: + assert(0); + } + } + +/** + * \ingroup Core_Parse + * \brief Parse a version 3 signature. + * + * This function parses an version 3 signature packet, handling RSA and DSA signatures. + * + * Once the signature has been parsed successfully, it is passed to the callback. + * + * \param *ptag Pointer to the Packet Tag. This function should consume the entire packet. + * \param *reader Our reader + * \param *cb The callback + * \return 1 on success, 0 on error + * + * \see RFC4880 5.2.2 + */ +static int parse_v3_signature(ops_region_t *region, + ops_parse_info_t *pinfo) + { + unsigned char c[1]=""; + ops_parser_content_t content; + + // clear signature + memset(&C.signature,'\0',sizeof C.signature); + + C.signature.info.version=OPS_V3; + + /* hash info length */ + if(!limited_read(c,1,region,pinfo)) + return 0; + if(c[0] != 5) + ERRP(pinfo,"bad hash info length"); + + if(!limited_read(c,1,region,pinfo)) + return 0; + C.signature.info.type=c[0]; + /* XXX: check signature type */ + + if(!limited_read_time(&C.signature.info.creation_time,region,pinfo)) + return 0; + C.signature.info.creation_time_set=ops_true; + + if(!limited_read(C.signature.info.signer_id,OPS_KEY_ID_SIZE,region,pinfo)) + return 0; + C.signature.info.signer_id_set=ops_true; + + if(!limited_read(c,1,region,pinfo)) + return 0; + C.signature.info.key_algorithm=c[0]; + /* XXX: check algorithm */ + + if(!limited_read(c,1,region,pinfo)) + return 0; + C.signature.info.hash_algorithm=c[0]; + /* XXX: check algorithm */ + + if(!limited_read(C.signature.hash2,2,region,pinfo)) + return 0; + + switch(C.signature.info.key_algorithm) + { + case OPS_PKA_RSA: + case OPS_PKA_RSA_SIGN_ONLY: + if(!limited_read_mpi(&C.signature.info.signature.rsa.sig,region,pinfo)) + return 0; + break; + + case OPS_PKA_DSA: + if(!limited_read_mpi(&C.signature.info.signature.dsa.r,region,pinfo) + || !limited_read_mpi(&C.signature.info.signature.dsa.s,region,pinfo)) + return 0; + break; + + case OPS_PKA_ELGAMAL_ENCRYPT_OR_SIGN: + if(!limited_read_mpi(&C.signature.info.signature.elgamal.r,region,pinfo) + || !limited_read_mpi(&C.signature.info.signature.elgamal.s,region,pinfo)) + return 0; + break; + + default: + OPS_ERROR_1(&pinfo->errors,OPS_E_ALG_UNSUPPORTED_SIGNATURE_ALG, + "Unsupported signature key algorithm (%s)", + ops_show_pka(C.signature.info.key_algorithm)); + return 0; + } + + if(region->length_read != region->length) + { + OPS_ERROR_1(&pinfo->errors,OPS_E_R_UNCONSUMED_DATA,"Unconsumed data (%d)",region->length-region->length_read); + return 0; + } + + if(C.signature.info.signer_id_set) + C.signature.hash=ops_parse_hash_find(pinfo,C.signature.info.signer_id); + + CBP(pinfo,OPS_PTAG_CT_SIGNATURE,&content); + + return 1; + } + +/** + * \ingroup Core_ReadPackets + * \brief Parse one signature sub-packet. + * + * Version 4 signatures can have an arbitrary amount of (hashed and unhashed) subpackets. Subpackets are used to hold + * optional attributes of subpackets. + * + * This function parses one such signature subpacket. + * + * Once the subpacket has been parsed successfully, it is passed to the callback. + * + * \param *ptag Pointer to the Packet Tag. This function should consume the entire subpacket. + * \param *reader Our reader + * \param *cb The callback + * \return 1 on success, 0 on error + * + * \see RFC4880 5.2.3 + */ +static int parse_one_signature_subpacket(ops_signature_t *sig, + ops_region_t *region, + ops_parse_info_t *pinfo) + { + ops_region_t subregion; + unsigned char c[1]=""; + ops_parser_content_t content; + unsigned t8,t7; + ops_boolean_t read=ops_true; + unsigned char bool[1]=""; + + ops_init_subregion(&subregion,region); + if(!limited_read_new_length(&subregion.length,region,pinfo)) + return 0; + + if(subregion.length > region->length) + ERRP(pinfo,"Subpacket too long"); + + if(!limited_read(c,1,&subregion,pinfo)) + return 0; + + t8=(c[0]&0x7f)/8; + t7=1 << (c[0]&7); + + content.critical=c[0] >> 7; + content.tag=OPS_PTAG_SIGNATURE_SUBPACKET_BASE+(c[0]&0x7f); + + /* Application wants it delivered raw */ + if(pinfo->ss_raw[t8]&t7) + { + C.ss_raw.tag=content.tag; + C.ss_raw.length=subregion.length-1; + C.ss_raw.raw=malloc(C.ss_raw.length); + if(!limited_read(C.ss_raw.raw,C.ss_raw.length,&subregion,pinfo)) + return 0; + CBP(pinfo,OPS_PTAG_RAW_SS,&content); + return 1; + } + + switch(content.tag) + { + case OPS_PTAG_SS_CREATION_TIME: + case OPS_PTAG_SS_EXPIRATION_TIME: + case OPS_PTAG_SS_KEY_EXPIRATION_TIME: + if(!limited_read_time(&C.ss_time.time,&subregion,pinfo)) + return 0; + if(content.tag == OPS_PTAG_SS_CREATION_TIME) + { + sig->info.creation_time=C.ss_time.time; + sig->info.creation_time_set=ops_true; + } + break; + + case OPS_PTAG_SS_TRUST: + if(!limited_read(&C.ss_trust.level,1,&subregion,pinfo) + || !limited_read(&C.ss_trust.amount,1,&subregion,pinfo)) + return 0; + break; + + case OPS_PTAG_SS_REVOCABLE: + if(!limited_read(bool,1,&subregion,pinfo)) + return 0; + C.ss_revocable.revocable=!!bool[0]; + break; + + case OPS_PTAG_SS_ISSUER_KEY_ID: + if(!limited_read(C.ss_issuer_key_id.key_id,OPS_KEY_ID_SIZE, + &subregion,pinfo)) + return 0; + memcpy(sig->info.signer_id,C.ss_issuer_key_id.key_id,OPS_KEY_ID_SIZE); + sig->info.signer_id_set=ops_true; + break; + + case OPS_PTAG_SS_PREFERRED_SKA: + if(!read_data(&C.ss_preferred_ska.data,&subregion,pinfo)) + return 0; + break; + + case OPS_PTAG_SS_PREFERRED_HASH: + if(!read_data(&C.ss_preferred_hash.data,&subregion,pinfo)) + return 0; + break; + + case OPS_PTAG_SS_PREFERRED_COMPRESSION: + if(!read_data(&C.ss_preferred_compression.data,&subregion,pinfo)) + return 0; + break; + + case OPS_PTAG_SS_PRIMARY_USER_ID: + if(!limited_read (bool,1,&subregion,pinfo)) + return 0; + C.ss_primary_user_id.primary_user_id = !!bool[0]; + break; + + case OPS_PTAG_SS_KEY_FLAGS: + if(!read_data(&C.ss_key_flags.data,&subregion,pinfo)) + return 0; + break; + + case OPS_PTAG_SS_KEY_SERVER_PREFS: + if(!read_data(&C.ss_key_server_prefs.data,&subregion,pinfo)) + return 0; + break; + + case OPS_PTAG_SS_FEATURES: + if(!read_data(&C.ss_features.data,&subregion,pinfo)) + return 0; + break; + + case OPS_PTAG_SS_SIGNERS_USER_ID: + if(!read_unsigned_string(&C.ss_signers_user_id.user_id,&subregion,pinfo)) + return 0; + break; + + case OPS_PTAG_SS_EMBEDDED_SIGNATURE: + // \todo should do something with this sig? + if (!read_data(&C.ss_embedded_signature.sig,&subregion,pinfo)) + return 0; + break; + + case OPS_PTAG_SS_NOTATION_DATA: + if(!limited_read_data(&C.ss_notation_data.flags,4,&subregion,pinfo)) + return 0; + if(!limited_read_size_t_scalar(&C.ss_notation_data.name.len,2, + &subregion,pinfo)) + return 0; + if(!limited_read_size_t_scalar(&C.ss_notation_data.value.len,2, + &subregion,pinfo)) + return 0; + if(!limited_read_data(&C.ss_notation_data.name, + C.ss_notation_data.name.len,&subregion,pinfo)) + return 0; + if(!limited_read_data(&C.ss_notation_data.value, + C.ss_notation_data.value.len,&subregion,pinfo)) + return 0; + break; + + case OPS_PTAG_SS_POLICY_URI: + if(!read_string(&C.ss_policy_url.text,&subregion,pinfo)) + return 0; + break; + + case OPS_PTAG_SS_REGEXP: + if(!read_string(&C.ss_regexp.text,&subregion, pinfo)) + return 0; + break; + + case OPS_PTAG_SS_PREFERRED_KEY_SERVER: + if(!read_string(&C.ss_preferred_key_server.text,&subregion,pinfo)) + return 0; + break; + + case OPS_PTAG_SS_USERDEFINED00: + case OPS_PTAG_SS_USERDEFINED01: + case OPS_PTAG_SS_USERDEFINED02: + case OPS_PTAG_SS_USERDEFINED03: + case OPS_PTAG_SS_USERDEFINED04: + case OPS_PTAG_SS_USERDEFINED05: + case OPS_PTAG_SS_USERDEFINED06: + case OPS_PTAG_SS_USERDEFINED07: + case OPS_PTAG_SS_USERDEFINED08: + case OPS_PTAG_SS_USERDEFINED09: + case OPS_PTAG_SS_USERDEFINED10: + if(!read_data(&C.ss_userdefined.data,&subregion,pinfo)) + return 0; + break; + + case OPS_PTAG_SS_RESERVED: + if(!read_data(&C.ss_unknown.data,&subregion,pinfo)) + return 0; + break; + + case OPS_PTAG_SS_REVOCATION_REASON: + /* first byte is the machine-readable code */ + if(!limited_read(&C.ss_revocation_reason.code,1,&subregion,pinfo)) + return 0; + + /* the rest is a human-readable UTF-8 string */ + if(!read_string(&C.ss_revocation_reason.text,&subregion,pinfo)) + return 0; + break; + + case OPS_PTAG_SS_REVOCATION_KEY: + /* octet 0 = class. Bit 0x80 must be set */ + if(!limited_read (&C.ss_revocation_key.class,1,&subregion,pinfo)) + return 0; + if(!(C.ss_revocation_key.class&0x80)) + { + printf("Warning: OPS_PTAG_SS_REVOCATION_KEY class: " + "Bit 0x80 should be set\n"); + return 0; + } + + /* octet 1 = algid */ + if(!limited_read(&C.ss_revocation_key.algid,1,&subregion,pinfo)) + return 0; + + /* octets 2-21 = fingerprint */ + if(!limited_read(&C.ss_revocation_key.fingerprint[0],20,&subregion, + pinfo)) + return 0; + break; + + default: + if(pinfo->ss_parsed[t8]&t7) + OPS_ERROR_1(&pinfo->errors, OPS_E_PROTO_UNKNOWN_SS, + "Unknown signature subpacket type (%d)", c[0]&0x7f); + read=ops_false; + break; + } + + /* Application doesn't want it delivered parsed */ + if(!(pinfo->ss_parsed[t8]&t7)) + { + if(content.critical) + OPS_ERROR_1(&pinfo->errors,OPS_E_PROTO_CRITICAL_SS_IGNORED, + "Critical signature subpacket ignored (%d)", + c[0]&0x7f); + if(!read && !limited_skip(subregion.length-1,&subregion,pinfo)) + return 0; + // printf("skipped %d length %d\n",c[0]&0x7f,subregion.length); + if(read) + ops_parser_content_free(&content); + return 1; + } + + if(read && subregion.length_read != subregion.length) + { + OPS_ERROR_1(&pinfo->errors,OPS_E_R_UNCONSUMED_DATA, + "Unconsumed data (%d)", + subregion.length-subregion.length_read); + return 0; + } + + CBP(pinfo,content.tag,&content); + + return 1; + } + +/** + \ingroup Core_Create + \brief Free the memory used when parsing this signature sub-packet type + \param ss_preferred_ska +*/ +void ops_ss_preferred_ska_free(ops_ss_preferred_ska_t *ss_preferred_ska) + { + data_free(&ss_preferred_ska->data); + } + +/** + \ingroup Core_Create + \brief Free the memory used when parsing this signature sub-packet type + \param ss_preferred_hash +*/ +void ops_ss_preferred_hash_free(ops_ss_preferred_hash_t *ss_preferred_hash) + { + data_free(&ss_preferred_hash->data); + } + +/** + \ingroup Core_Create + \brief Free the memory used when parsing this signature sub-packet type +*/ +void ops_ss_preferred_compression_free(ops_ss_preferred_compression_t *ss_preferred_compression) + { + data_free(&ss_preferred_compression->data); + } + +/** + \ingroup Core_Create + \brief Free the memory used when parsing this signature sub-packet type +*/ +void ops_ss_key_flags_free(ops_ss_key_flags_t *ss_key_flags) + { + data_free(&ss_key_flags->data); + } + +/** + \ingroup Core_Create + \brief Free the memory used when parsing this signature sub-packet type +*/ +void ops_ss_features_free(ops_ss_features_t *ss_features) + { + data_free(&ss_features->data); + } + +/** + \ingroup Core_Create + \brief Free the memory used when parsing this signature sub-packet type +*/ +void ops_ss_key_server_prefs_free(ops_ss_key_server_prefs_t *ss_key_server_prefs) + { + data_free(&ss_key_server_prefs->data); + } + +/** + * \ingroup Core_ReadPackets + * \brief Parse several signature subpackets. + * + * Hashed and unhashed subpacket sets are preceded by an octet count that specifies the length of the complete set. + * This function parses this length and then calls parse_one_signature_subpacket() for each subpacket until the + * entire set is consumed. + * + * This function does not call the callback directly, parse_one_signature_subpacket() does for each subpacket. + * + * \param *ptag Pointer to the Packet Tag. + * \param *reader Our reader + * \param *cb The callback + * \return 1 on success, 0 on error + * + * \see RFC4880 5.2.3 + */ +static int parse_signature_subpackets(ops_signature_t *sig, + ops_region_t *region, + ops_parse_info_t *pinfo) + { + ops_region_t subregion; + ops_parser_content_t content; + + ops_init_subregion(&subregion,region); + if(!limited_read_scalar(&subregion.length,2,region,pinfo)) + return 0; + + if(subregion.length > region->length) + ERRP(pinfo,"Subpacket set too long"); + + while(subregion.length_read < subregion.length) + if(!parse_one_signature_subpacket(sig,&subregion,pinfo)) + return 0; + + if(subregion.length_read != subregion.length) + { + if(!limited_skip(subregion.length-subregion.length_read,&subregion, + pinfo)) + ERRP(pinfo,"Read failed while recovering from subpacket length mismatch"); + ERRP(pinfo,"Subpacket length mismatch"); + } + + return 1; + } + +/** + * \ingroup Core_ReadPackets + * \brief Parse a version 4 signature. + * + * This function parses a version 4 signature including all its hashed and unhashed subpackets. + * + * Once the signature packet has been parsed successfully, it is passed to the callback. + * + * \param *ptag Pointer to the Packet Tag. + * \param *reader Our reader + * \param *cb The callback + * \return 1 on success, 0 on error + * + * \see RFC4880 5.2.3 + */ +static int parse_v4_signature(ops_region_t *region,ops_parse_info_t *pinfo) + { + unsigned char c[1]=""; + ops_parser_content_t content; + + //debug=1; + if (debug) + { fprintf(stderr, "\nparse_v4_signature\n"); } + + // clear signature + memset(&C.signature,'\0',sizeof C.signature); + + /* We need to hash the packet data from version through the hashed subpacket data */ + + C.signature.v4_hashed_data_start=pinfo->rinfo.alength-1; + + /* Set version,type,algorithms */ + + C.signature.info.version=OPS_V4; + + if(!limited_read(c,1,region,pinfo)) + return 0; + C.signature.info.type=c[0]; + if (debug) + { fprintf(stderr, "signature type=%d\n", C.signature.info.type); } + + /* XXX: check signature type */ + + if(!limited_read(c,1,region,pinfo)) + return 0; + C.signature.info.key_algorithm=c[0]; + /* XXX: check algorithm */ + if (debug) + { fprintf(stderr, "key_algorithm=%d\n", C.signature.info.key_algorithm); } + + if(!limited_read(c,1,region,pinfo)) + return 0; + C.signature.info.hash_algorithm=c[0]; + /* XXX: check algorithm */ + if (debug) + { fprintf(stderr, "hash_algorithm=%d %s\n", C.signature.info.hash_algorithm, ops_show_hash_algorithm(C.signature.info.hash_algorithm)); } + + CBP(pinfo,OPS_PTAG_CT_SIGNATURE_HEADER,&content); + + if(!parse_signature_subpackets(&C.signature,region,pinfo)) + return 0; + + C.signature.info.v4_hashed_data_length=pinfo->rinfo.alength + -C.signature.v4_hashed_data_start; + + // copy hashed subpackets + if (C.signature.info.v4_hashed_data) + free(C.signature.info.v4_hashed_data); + C.signature.info.v4_hashed_data=ops_mallocz(C.signature.info.v4_hashed_data_length); + + if (!pinfo->rinfo.accumulate) + { + /* We must accumulate, else we can't check the signature */ + fprintf(stderr,"*** ERROR: must set accumulate to true\n"); + assert(0); + } + + memcpy(C.signature.info.v4_hashed_data, + pinfo->rinfo.accumulated+C.signature.v4_hashed_data_start, + C.signature.info.v4_hashed_data_length); + + if(!parse_signature_subpackets(&C.signature,region,pinfo)) + return 0; + + if(!limited_read(C.signature.hash2,2,region,pinfo)) + return 0; + + switch(C.signature.info.key_algorithm) + { + case OPS_PKA_RSA: + if(!limited_read_mpi(&C.signature.info.signature.rsa.sig,region,pinfo)) + return 0; + break; + + case OPS_PKA_DSA: + if(!limited_read_mpi(&C.signature.info.signature.dsa.r,region,pinfo)) + ERRP(pinfo,"Error reading DSA r field in signature"); + if (!limited_read_mpi(&C.signature.info.signature.dsa.s,region,pinfo)) + ERRP(pinfo,"Error reading DSA s field in signature"); + break; + + case OPS_PKA_ELGAMAL_ENCRYPT_OR_SIGN: + if(!limited_read_mpi(&C.signature.info.signature.elgamal.r,region,pinfo) + || !limited_read_mpi(&C.signature.info.signature.elgamal.s,region,pinfo)) + return 0; + break; + + case OPS_PKA_PRIVATE00: + case OPS_PKA_PRIVATE01: + case OPS_PKA_PRIVATE02: + case OPS_PKA_PRIVATE03: + case OPS_PKA_PRIVATE04: + case OPS_PKA_PRIVATE05: + case OPS_PKA_PRIVATE06: + case OPS_PKA_PRIVATE07: + case OPS_PKA_PRIVATE08: + case OPS_PKA_PRIVATE09: + case OPS_PKA_PRIVATE10: + if (!read_data(&C.signature.info.signature.unknown.data,region,pinfo)) + return 0; + break; + + default: + OPS_ERROR_1(&pinfo->errors,OPS_E_ALG_UNSUPPORTED_SIGNATURE_ALG, + "Bad v4 signature key algorithm (%s)", + ops_show_pka(C.signature.info.key_algorithm)); + return 0; + } + + if(region->length_read != region->length) + { + OPS_ERROR_1(&pinfo->errors,OPS_E_R_UNCONSUMED_DATA, + "Unconsumed data (%d)", + region->length-region->length_read); + return 0; + } + + CBP(pinfo,OPS_PTAG_CT_SIGNATURE_FOOTER,&content); + + return 1; + } + +/** + * \ingroup Core_ReadPackets + * \brief Parse a signature subpacket. + * + * This function calls the appropriate function to handle v3 or v4 signatures. + * + * Once the signature packet has been parsed successfully, it is passed to the callback. + * + * \param *ptag Pointer to the Packet Tag. + * \param *reader Our reader + * \param *cb The callback + * \return 1 on success, 0 on error + */ +static int parse_signature(ops_region_t *region,ops_parse_info_t *pinfo) + { + unsigned char c[1]=""; + ops_parser_content_t content; + + assert(region->length_read == 0); /* We should not have read anything so far */ + + memset(&content,'\0',sizeof content); + + if(!limited_read(c,1,region,pinfo)) + return 0; + + if(c[0] == 2 || c[0] == 3) + return parse_v3_signature(region,pinfo); + else if(c[0] == 4) + return parse_v4_signature(region,pinfo); + + OPS_ERROR_1(&pinfo->errors,OPS_E_PROTO_BAD_SIGNATURE_VRSN, + "Bad signature version (%d)",c[0]); + return 0; + } + +/** + \ingroup Core_ReadPackets + \brief Parse Compressed packet +*/ +static int parse_compressed(ops_region_t *region,ops_parse_info_t *pinfo) + { + unsigned char c[1]=""; + ops_parser_content_t content; + + if(!limited_read(c,1,region,pinfo)) + return 0; + + C.compressed.type=c[0]; + + CBP(pinfo,OPS_PTAG_CT_COMPRESSED,&content); + + /* The content of a compressed data packet is more OpenPGP packets + once decompressed, so recursively handle them */ + + return ops_decompress(region,pinfo,C.compressed.type); + } + +/** + \ingroup Core_ReadPackets + \brief Parse a One Pass Signature packet +*/ +static int parse_one_pass(ops_region_t *region,ops_parse_info_t *pinfo) + { + unsigned char c[1]=""; + ops_parser_content_t content; + + if(!limited_read(&C.one_pass_signature.version,1,region,pinfo)) + return 0; + if(C.one_pass_signature.version != 3) + { + OPS_ERROR_1(&pinfo->errors,OPS_E_PROTO_BAD_ONE_PASS_SIG_VRSN, + "Bad one-pass signature version (%d)", + C.one_pass_signature.version); + return 0; + } + + if(!limited_read(c,1,region,pinfo)) + return 0; + C.one_pass_signature.sig_type=c[0]; + + if(!limited_read(c,1,region,pinfo)) + return 0; + C.one_pass_signature.hash_algorithm=c[0]; + + if(!limited_read(c,1,region,pinfo)) + return 0; + C.one_pass_signature.key_algorithm=c[0]; + + if(!limited_read(C.one_pass_signature.keyid, + sizeof C.one_pass_signature.keyid,region,pinfo)) + return 0; + + if(!limited_read(c,1,region,pinfo)) + return 0; + C.one_pass_signature.nested=!!c[0]; + + CBP(pinfo,OPS_PTAG_CT_ONE_PASS_SIGNATURE,&content); + + // XXX: we should, perhaps, let the app choose whether to hash or not + ops_parse_hash_init(pinfo,C.one_pass_signature.hash_algorithm, + C.one_pass_signature.keyid); + + return 1; + } + +/** + \ingroup Core_Create + \brief Free the memory used when parsing this signature sub-packet type +*/ +void ops_ss_userdefined_free(ops_ss_userdefined_t *ss_userdefined) + { + data_free(&ss_userdefined->data); + } + +/** + \ingroup Core_Create + \brief Free the memory used when parsing this signature sub-packet type +*/ +void ops_ss_reserved_free(ops_ss_unknown_t *ss_unknown) + { + data_free(&ss_unknown->data); + } + +/** + \ingroup Core_Create + \brief Free the memory used when parsing this signature sub-packet type +*/ +void ops_ss_notation_data_free(ops_ss_notation_data_t *ss_notation_data) + { + data_free(&ss_notation_data->name); + data_free(&ss_notation_data->value); + } + +void ops_ss_embedded_signature_free(ops_ss_embedded_signature_t *ss_embedded_signature) + { + data_free(&ss_embedded_signature->sig); + } + +/** + \ingroup Core_Create + \brief Free the memory used when parsing this signature sub-packet type +*/ +void ops_ss_revocation_reason_free(ops_ss_revocation_reason_t *ss_revocation_reason) + { + string_free(&ss_revocation_reason->text); + } + +/** + \ingroup Core_Create + \brief Free the memory used when parsing this packet type +*/ +void ops_trust_free(ops_trust_t *trust) + { + data_free(&trust->data); + } + +/** + \ingroup Core_ReadPackets + \brief Parse a Trust packet +*/ +static int +parse_trust (ops_region_t *region, ops_parse_info_t *pinfo) + { + ops_parser_content_t content; + + if(!read_data(&C.trust.data,region,pinfo)) + return 0; + + CBP(pinfo,OPS_PTAG_CT_TRUST, &content); + + return 1; + } + +/** + \ingroup Core_ReadPackets + \brief Parse a Literal Data packet +*/ +static int parse_literal_data(ops_region_t *region,ops_parse_info_t *pinfo) + { + ops_parser_content_t content; + unsigned char c[1]=""; + + if(!limited_read(c,1,region,pinfo)) + return 0; + C.literal_data_header.format=c[0]; + + if(!limited_read(c,1,region,pinfo)) + return 0; + if(!limited_read((unsigned char *)C.literal_data_header.filename,c[0], + region,pinfo)) + return 0; + C.literal_data_header.filename[c[0]]='\0'; + + if(!limited_read_time(&C.literal_data_header.modification_time,region,pinfo)) + return 0; + + CBP(pinfo,OPS_PTAG_CT_LITERAL_DATA_HEADER,&content); + + while(region->length_read < region->length) + { + unsigned l=region->length-region->length_read; + + if(l > sizeof C.literal_data_body.data) + l=sizeof C.literal_data_body.data; + + if(!limited_read(C.literal_data_body.data,l,region,pinfo)) + return 0; + + C.literal_data_body.length=l; + + ops_parse_hash_data(pinfo,C.literal_data_body.data,l); + + CBP(pinfo,OPS_PTAG_CT_LITERAL_DATA_BODY,&content); + } + + return 1; + } + +/** + * \ingroup Core_Create + * + * ops_secret_key_free() frees the memory associated with "key". Note that + * the key itself is not freed. + * + * \param key + */ + +void ops_secret_key_free(ops_secret_key_t *key) + { + switch(key->public_key.algorithm) + { + case OPS_PKA_RSA: + case OPS_PKA_RSA_ENCRYPT_ONLY: + case OPS_PKA_RSA_SIGN_ONLY: + free_BN(&key->key.rsa.d); + free_BN(&key->key.rsa.p); + free_BN(&key->key.rsa.q); + free_BN(&key->key.rsa.u); + break; + + case OPS_PKA_DSA: + free_BN(&key->key.dsa.x); + break; + + default: + fprintf(stderr,"ops_secret_key_free: Unknown algorithm: %d (%s)\n",key->public_key.algorithm, ops_show_pka(key->public_key.algorithm)); + //assert(0); + } + + ops_public_key_free(&key->public_key); + } + +static int consume_packet(ops_region_t *region,ops_parse_info_t *pinfo, + ops_boolean_t warn) + { + ops_data_t remainder; + ops_parser_content_t content; + + if(region->indeterminate) + ERRP(pinfo,"Can't consume indeterminate packets"); + + if(read_data(&remainder,region,pinfo)) + { + /* now throw it away */ + data_free(&remainder); + if(warn) + OPS_ERROR(&pinfo->errors,OPS_E_P_PACKET_CONSUMED,"Warning: packet consumer"); + } + else if(warn) + OPS_ERROR(&pinfo->errors,OPS_E_P_PACKET_NOT_CONSUMED,"Warning: Packet was not consumed"); + else + { + OPS_ERROR(&pinfo->errors,OPS_E_P_PACKET_NOT_CONSUMED,"Packet was not consumed"); + return 0; + } + + return 1; + } + +/** + * \ingroup Core_ReadPackets + * \brief Parse a secret key + */ +static int parse_secret_key(ops_region_t *region,ops_parse_info_t *pinfo) + { + ops_parser_content_t content; + unsigned char c[1]=""; + ops_crypt_t decrypt; + int ret=1; + ops_region_t encregion; + ops_region_t *saved_region=NULL; + size_t checksum_length=2; + ops_hash_t checkhash; + int blocksize; + ops_boolean_t crypted; + + if (debug) + { fprintf(stderr,"\n---------\nparse_secret_key:\n"); + fprintf(stderr,"region length=%d, length_read=%d, remainder=%d\n", region->length, region->length_read, region->length-region->length_read); + } + + memset(&content,'\0',sizeof content); + if(!parse_public_key_data(&C.secret_key.public_key,region,pinfo)) + return 0; + + if (debug) + { + fprintf(stderr,"parse_secret_key: public key parsed\n"); + ops_print_public_key(&C.secret_key.public_key); + } + + pinfo->reading_v3_secret=C.secret_key.public_key.version != OPS_V4; + + if(!limited_read(c,1,region,pinfo)) + return 0; + C.secret_key.s2k_usage=c[0]; + if(C.secret_key.s2k_usage == OPS_S2KU_ENCRYPTED_AND_HASHED) + checksum_length=20; + + if(C.secret_key.s2k_usage == OPS_S2KU_ENCRYPTED + || C.secret_key.s2k_usage == OPS_S2KU_ENCRYPTED_AND_HASHED) + { + if(!limited_read(c,1,region,pinfo)) + return 0; + C.secret_key.algorithm=c[0]; + + if(!limited_read(c,1,region,pinfo)) + return 0; + C.secret_key.s2k_specifier=c[0]; + + assert(C.secret_key.s2k_specifier == OPS_S2KS_SIMPLE + || C.secret_key.s2k_specifier == OPS_S2KS_SALTED + || C.secret_key.s2k_specifier == OPS_S2KS_ITERATED_AND_SALTED); + + if(!limited_read(c,1,region,pinfo)) + return 0; + C.secret_key.hash_algorithm=c[0]; + + if(C.secret_key.s2k_specifier != OPS_S2KS_SIMPLE + && !limited_read(C.secret_key.salt,8,region,pinfo)) + { + return 0; + } + + if(C.secret_key.s2k_specifier == OPS_S2KS_ITERATED_AND_SALTED) + { + if(!limited_read(c,1,region,pinfo)) + return 0; + C.secret_key.octet_count=(16+(c[0]&15)) << ((c[0] >> 4)+6); + } + } + else if(C.secret_key.s2k_usage != OPS_S2KU_NONE) + { + // this is V3 style, looks just like a V4 simple hash + C.secret_key.algorithm=C.secret_key.s2k_usage; + C.secret_key.s2k_usage=OPS_S2KU_ENCRYPTED; + C.secret_key.s2k_specifier=OPS_S2KS_SIMPLE; + C.secret_key.hash_algorithm=OPS_HASH_MD5; + } + + crypted=C.secret_key.s2k_usage == OPS_S2KU_ENCRYPTED + || C.secret_key.s2k_usage == OPS_S2KU_ENCRYPTED_AND_HASHED; + + if(crypted) + { + int n; + ops_parser_content_t pc; + char *passphrase; + unsigned char key[OPS_MAX_KEY_SIZE+OPS_MAX_HASH_SIZE]; + ops_hash_t hashes[(OPS_MAX_KEY_SIZE+OPS_MIN_HASH_SIZE-1)/OPS_MIN_HASH_SIZE]; + int keysize; + int hashsize; + size_t l; + + blocksize=ops_block_size(C.secret_key.algorithm); + assert(blocksize > 0 && blocksize <= OPS_MAX_BLOCK_SIZE); + + if(!limited_read(C.secret_key.iv,blocksize,region,pinfo)) + return 0; + + memset(&pc,'\0',sizeof pc); + passphrase=NULL; + pc.content.secret_key_passphrase.passphrase=&passphrase; + pc.content.secret_key_passphrase.secret_key=&C.secret_key; + CBP(pinfo,OPS_PARSER_CMD_GET_SK_PASSPHRASE,&pc); + if(!passphrase) + { + if (debug) + { + // \todo make into proper error + fprintf(stderr,"parse_secret_key: can't get passphrase\n"); + } + + if(!consume_packet(region,pinfo,ops_false)) + return 0; + + CBP(pinfo,OPS_PTAG_CT_ENCRYPTED_SECRET_KEY,&content); + + return 1; + } + + keysize=ops_key_size(C.secret_key.algorithm); + assert(keysize > 0 && keysize <= OPS_MAX_KEY_SIZE); + + hashsize=ops_hash_size(C.secret_key.hash_algorithm); + assert(hashsize > 0 && hashsize <= OPS_MAX_HASH_SIZE); + + for(n=0 ; n*hashsize < keysize ; ++n) + { + int i; + + ops_hash_any(&hashes[n],C.secret_key.hash_algorithm); + hashes[n].init(&hashes[n]); + // preload hashes with zeroes... + for(i=0 ; i < n ; ++i) + hashes[n].add(&hashes[n],(unsigned char *)"",1); + } + + l=strlen(passphrase); + + for(n=0 ; n*hashsize < keysize ; ++n) + { + unsigned i; + + switch(C.secret_key.s2k_specifier) + { + case OPS_S2KS_SALTED: + hashes[n].add(&hashes[n],C.secret_key.salt,OPS_SALT_SIZE); + // flow through... + case OPS_S2KS_SIMPLE: + hashes[n].add(&hashes[n],(unsigned char*)passphrase,l); + break; + + case OPS_S2KS_ITERATED_AND_SALTED: + for(i=0 ; i < C.secret_key.octet_count ; i+=l+OPS_SALT_SIZE) + { + int j=l+OPS_SALT_SIZE; + + if(i+j > C.secret_key.octet_count && i != 0) + j=C.secret_key.octet_count-i; + + hashes[n].add(&hashes[n],C.secret_key.salt, + j > OPS_SALT_SIZE ? OPS_SALT_SIZE : j); + if(j > OPS_SALT_SIZE) + hashes[n].add(&hashes[n],(unsigned char *)passphrase,j-OPS_SALT_SIZE); + } + + } + } + + for(n=0 ; n*hashsize < keysize ; ++n) + { + int r=hashes[n].finish(&hashes[n],key+n*hashsize); + assert(r == hashsize); + } + + free(passphrase); + + ops_crypt_any(&decrypt,C.secret_key.algorithm); + if (debug) + { + unsigned int i=0; + fprintf(stderr,"\nREADING:\niv="); + for (i=0; ilength-region->length_read; + if(C.secret_key.public_key.version != OPS_V4) + { + encregion.length-=2; + } + saved_region=region; + region=&encregion; + } + + if(C.secret_key.s2k_usage == OPS_S2KU_ENCRYPTED_AND_HASHED) + { + ops_hash_sha1(&checkhash); + ops_reader_push_hash(pinfo,&checkhash); + } + else + { + ops_reader_push_sum16(pinfo); + } + + switch(C.secret_key.public_key.algorithm) + { + case OPS_PKA_RSA: + case OPS_PKA_RSA_ENCRYPT_ONLY: + case OPS_PKA_RSA_SIGN_ONLY: + if(!limited_read_mpi(&C.secret_key.key.rsa.d,region,pinfo) + || !limited_read_mpi(&C.secret_key.key.rsa.p,region,pinfo) + || !limited_read_mpi(&C.secret_key.key.rsa.q,region,pinfo) + || !limited_read_mpi(&C.secret_key.key.rsa.u,region,pinfo)) + ret=0; + + break; + + case OPS_PKA_DSA: + + if(!limited_read_mpi(&C.secret_key.key.dsa.x,region,pinfo)) + ret=0; + break; + + default: + OPS_ERROR_2(&pinfo->errors,OPS_E_ALG_UNSUPPORTED_PUBLIC_KEY_ALG,"Unsupported Public Key algorithm %d (%s)",C.secret_key.public_key.algorithm,ops_show_pka(C.secret_key.public_key.algorithm)); + ret=0; + // assert(0); + } + + if (debug) + { + fprintf(stderr,"4 MPIs read\n"); + // ops_print_secret_key_verbose(OPS_PTAG_CT_SECRET_KEY, &C.secret_key); + } + + pinfo->reading_v3_secret=ops_false; + + if(C.secret_key.s2k_usage == OPS_S2KU_ENCRYPTED_AND_HASHED) + { + unsigned char hash[20]; + + ops_reader_pop_hash(pinfo); + checkhash.finish(&checkhash,hash); + + if(crypted && C.secret_key.public_key.version != OPS_V4) + { + ops_reader_pop_decrypt(pinfo); + region=saved_region; + } + + if(ret) + { + if(!limited_read(C.secret_key.checkhash,20,region,pinfo)) + return 0; + + if(memcmp(hash,C.secret_key.checkhash,20)) + ERRP(pinfo,"Hash mismatch in secret key"); + } + } + else + { + unsigned short sum; + + sum=ops_reader_pop_sum16(pinfo); + + if(crypted && C.secret_key.public_key.version != OPS_V4) + { + ops_reader_pop_decrypt(pinfo); + region=saved_region; + } + + if(ret) + { + if(!limited_read_scalar(&C.secret_key.checksum,2,region, + pinfo)) + return 0; + + if(sum != C.secret_key.checksum) + ERRP(pinfo,"Checksum mismatch in secret key"); + } + } + + if(crypted && C.secret_key.public_key.version == OPS_V4) + { + ops_reader_pop_decrypt(pinfo); + } + + assert(!ret || region->length_read == region->length); + + if(!ret) + return 0; + + CBP(pinfo,OPS_PTAG_CT_SECRET_KEY,&content); + + if (debug) + { fprintf(stderr, "--- end of parse_secret_key\n\n"); } + + return 1; + } + +/** + \ingroup Core_ReadPackets + \brief Parse a Public Key Session Key packet +*/ +static int parse_pk_session_key(ops_region_t *region, + ops_parse_info_t *pinfo) + { + unsigned char c[1]=""; + ops_parser_content_t content; + ops_parser_content_t pc; + + int n; + BIGNUM *enc_m; + unsigned k; + const ops_secret_key_t *secret; + unsigned char cs[2]; + unsigned char* iv; + + // Can't rely on it being CAST5 + // \todo FIXME RW + // const size_t sz_unencoded_m_buf=CAST_KEY_LENGTH+1+2; + const size_t sz_unencoded_m_buf=1024; + unsigned char unencoded_m_buf[sz_unencoded_m_buf]; + + if(!limited_read(c,1,region,pinfo)) + return 0; + C.pk_session_key.version=c[0]; + if(C.pk_session_key.version != OPS_PKSK_V3) + { + OPS_ERROR_1(&pinfo->errors, OPS_E_PROTO_BAD_PKSK_VRSN, + "Bad public-key encrypted session key version (%d)", + C.pk_session_key.version); + return 0; + } + + if(!limited_read(C.pk_session_key.key_id, + sizeof C.pk_session_key.key_id,region,pinfo)) + return 0; + + if (debug) + { + int i; + int x=sizeof C.pk_session_key.key_id; + printf("session key: public key id: x=%d\n",x); + for (i=0; ierrors, OPS_E_ALG_UNSUPPORTED_PUBLIC_KEY_ALG, + "Unknown public key algorithm in session key (%s)", + ops_show_pka(C.pk_session_key.algorithm)); + return 0; + } + + memset(&pc,'\0',sizeof pc); + secret=NULL; + pc.content.get_secret_key.secret_key=&secret; + pc.content.get_secret_key.pk_session_key=&C.pk_session_key; + + CBP(pinfo,OPS_PARSER_CMD_GET_SECRET_KEY,&pc); + + if(!secret) + { + CBP(pinfo,OPS_PTAG_CT_ENCRYPTED_PK_SESSION_KEY,&content); + + return 1; + } + + // n=ops_decrypt_mpi(buf,sizeof buf,enc_m,secret); + n=ops_decrypt_and_unencode_mpi(unencoded_m_buf,sizeof unencoded_m_buf,enc_m,secret); + + if(n < 1) + { + ERRP(pinfo,"decrypted message too short"); + return 0; + } + + // PKA + C.pk_session_key.symmetric_algorithm=unencoded_m_buf[0]; + + if (!ops_is_sa_supported(C.pk_session_key.symmetric_algorithm)) + { + // ERR1P + OPS_ERROR_1(&pinfo->errors,OPS_E_ALG_UNSUPPORTED_SYMMETRIC_ALG, + "Symmetric algorithm %s not supported", + ops_show_symmetric_algorithm(C.pk_session_key.symmetric_algorithm)); + return 0; + } + + k=ops_key_size(C.pk_session_key.symmetric_algorithm); + + if((unsigned)n != k+3) + { + OPS_ERROR_2(&pinfo->errors,OPS_E_PROTO_DECRYPTED_MSG_WRONG_LEN, + "decrypted message wrong length (got %d expected %d)", + n,k+3); + return 0; + } + + assert(k <= sizeof C.pk_session_key.key); + + memcpy(C.pk_session_key.key,unencoded_m_buf+1,k); + + if (debug) + { + printf("session key recovered (len=%d):\n",k); + unsigned int j; + for(j=0; jerrors, OPS_E_PROTO_BAD_SK_CHECKSUM, + "Session key checksum wrong: expected %2x %2x, got %2x %2x", + cs[0], cs[1], unencoded_m_buf[k+1], unencoded_m_buf[k+2]); + return 0; + } + + // all is well + CBP(pinfo,OPS_PTAG_CT_PK_SESSION_KEY,&content); + + ops_crypt_any(&pinfo->decrypt,C.pk_session_key.symmetric_algorithm); + iv=ops_mallocz(pinfo->decrypt.blocksize); + pinfo->decrypt.set_iv(&pinfo->decrypt, iv); + pinfo->decrypt.set_key(&pinfo->decrypt,C.pk_session_key.key); + ops_encrypt_init(&pinfo->decrypt); + return 1; + } + +// XXX: make this static? +int ops_decrypt_se_data(ops_content_tag_t tag,ops_region_t *region, + ops_parse_info_t *pinfo) + { + int r=1; + ops_crypt_t *decrypt=ops_parse_get_decrypt(pinfo); + + if(decrypt) + { + unsigned char buf[OPS_MAX_BLOCK_SIZE+2]=""; + size_t b=decrypt->blocksize; + // ops_parser_content_t content; + ops_region_t encregion; + + + ops_reader_push_decrypt(pinfo,decrypt,region); + + ops_init_subregion(&encregion,NULL); + encregion.length=b+2; + + if(!exact_limited_read(buf,b+2,&encregion,pinfo)) + return 0; + + if(buf[b-2] != buf[b] || buf[b-1] != buf[b+1]) + { + ops_reader_pop_decrypt(pinfo); + OPS_ERROR_4(&pinfo->errors, OPS_E_PROTO_BAD_SYMMETRIC_DECRYPT, + "Bad symmetric decrypt (%02x%02x vs %02x%02x)", + buf[b-2],buf[b-1],buf[b],buf[b+1]); + return 0; + } + + if(tag == OPS_PTAG_CT_SE_DATA_BODY) + { + decrypt->decrypt_resync(decrypt); + decrypt->block_encrypt(decrypt,decrypt->civ,decrypt->civ); + } + + + r=ops_parse(pinfo); + + ops_reader_pop_decrypt(pinfo); + } + else + { + ops_parser_content_t content; + + while(region->length_read < region->length) + { + unsigned l=region->length-region->length_read; + + if(l > sizeof C.se_data_body.data) + l=sizeof C.se_data_body.data; + + if(!limited_read(C.se_data_body.data,l,region,pinfo)) + return 0; + + C.se_data_body.length=l; + + CBP(pinfo,tag,&content); + } + } + + return r; + } + +int ops_decrypt_se_ip_data(ops_content_tag_t tag,ops_region_t *region, + ops_parse_info_t *pinfo) + { + int r=1; + ops_crypt_t *decrypt=ops_parse_get_decrypt(pinfo); + + if(decrypt) + { + ops_reader_push_decrypt(pinfo,decrypt,region); + ops_reader_push_se_ip_data(pinfo,decrypt,region); + + r=ops_parse(pinfo); + + // assert(0); + ops_reader_pop_se_ip_data(pinfo); + ops_reader_pop_decrypt(pinfo); + } + else + { + ops_parser_content_t content; + + while(region->length_read < region->length) + { + unsigned l=region->length-region->length_read; + + if(l > sizeof C.se_data_body.data) + l=sizeof C.se_data_body.data; + + if(!limited_read(C.se_data_body.data,l,region,pinfo)) + return 0; + + C.se_data_body.length=l; + + CBP(pinfo,tag,&content); + } + } + + return r; + } + +/** + \ingroup Core_ReadPackets + \brief Read a Symmetrically Encrypted packet +*/ +static int parse_se_data(ops_region_t *region,ops_parse_info_t *pinfo) + { + ops_parser_content_t content; + + /* there's no info to go with this, so just announce it */ + CBP(pinfo,OPS_PTAG_CT_SE_DATA_HEADER,&content); + + /* The content of an encrypted data packet is more OpenPGP packets + once decrypted, so recursively handle them */ + return ops_decrypt_se_data(OPS_PTAG_CT_SE_DATA_BODY,region,pinfo); + } + +/** + \ingroup Core_ReadPackets + \brief Read a Symmetrically Encrypted Integrity Protected packet +*/ +static int parse_se_ip_data(ops_region_t *region,ops_parse_info_t *pinfo) + { + unsigned char c[1]=""; + ops_parser_content_t content; + + if(!limited_read(c,1,region,pinfo)) + return 0; + C.se_ip_data_header.version=c[0]; + assert(C.se_ip_data_header.version == OPS_SE_IP_V1); + + /* The content of an encrypted data packet is more OpenPGP packets + once decrypted, so recursively handle them */ + return ops_decrypt_se_ip_data(OPS_PTAG_CT_SE_IP_DATA_BODY,region,pinfo); + } + +/** + \ingroup Core_ReadPackets + \brief Read a MDC packet +*/ +static int parse_mdc(ops_region_t *region, ops_parse_info_t *pinfo) + { + ops_parser_content_t content; + + if (!limited_read((unsigned char *)&C.mdc,OPS_SHA1_HASH_SIZE,region,pinfo)) + return 0; + + CBP(pinfo,OPS_PTAG_CT_MDC,&content); + + return 1; + } + +/** + * \ingroup Core_ReadPackets + * \brief Parse one packet. + * + * This function parses the packet tag. It computes the value of the + * content tag and then calls the appropriate function to handle the + * content. + * + * \param *pinfo How to parse + * \param *pktlen On return, will contain number of bytes in packet + * \return 1 on success, 0 on error, -1 on EOF */ +static int ops_parse_one_packet(ops_parse_info_t *pinfo, + unsigned long *pktlen) + { + unsigned char ptag[1]; + ops_parser_content_t content; + int r; + ops_region_t region; + ops_boolean_t indeterminate=ops_false; + + C.ptag.position=pinfo->rinfo.position; + + r=base_read(ptag,1,pinfo); + + // errors in the base read are effectively EOF. + if(r <= 0) + return -1; + + *pktlen=0; + + if(!(*ptag&OPS_PTAG_ALWAYS_SET)) + { + C.error.error="Format error (ptag bit not set)"; + CBP(pinfo,OPS_PARSER_ERROR,&content); + return 0; + } + C.ptag.new_format=!!(*ptag&OPS_PTAG_NEW_FORMAT); + if(C.ptag.new_format) + { + C.ptag.content_tag=*ptag&OPS_PTAG_NF_CONTENT_TAG_MASK; + C.ptag.length_type=0; + if(!read_new_length(&C.ptag.length,pinfo)) + return 0; + + } + else + { + ops_boolean_t rb; + + C.ptag.content_tag=(*ptag&OPS_PTAG_OF_CONTENT_TAG_MASK) + >> OPS_PTAG_OF_CONTENT_TAG_SHIFT; + C.ptag.length_type=*ptag&OPS_PTAG_OF_LENGTH_TYPE_MASK; + switch(C.ptag.length_type) + { + case OPS_PTAG_OF_LT_ONE_BYTE: + rb=_read_scalar(&C.ptag.length,1,pinfo); + break; + + case OPS_PTAG_OF_LT_TWO_BYTE: + rb=_read_scalar(&C.ptag.length,2,pinfo); + break; + + case OPS_PTAG_OF_LT_FOUR_BYTE: + rb=_read_scalar(&C.ptag.length,4,pinfo); + break; + + case OPS_PTAG_OF_LT_INDETERMINATE: + C.ptag.length=0; + indeterminate=ops_true; + rb=ops_true; + break; + } + if(!rb) + return 0; + } + + CBP(pinfo,OPS_PARSER_PTAG,&content); + + ops_init_subregion(®ion,NULL); + region.length=C.ptag.length; + region.indeterminate=indeterminate; + switch(C.ptag.content_tag) + { + case OPS_PTAG_CT_SIGNATURE: + r=parse_signature(®ion,pinfo); + break; + + case OPS_PTAG_CT_PUBLIC_KEY: + case OPS_PTAG_CT_PUBLIC_SUBKEY: + r=parse_public_key(C.ptag.content_tag,®ion,pinfo); + break; + + case OPS_PTAG_CT_TRUST: + r=parse_trust(®ion, pinfo); + break; + + case OPS_PTAG_CT_USER_ID: + r=parse_user_id(®ion,pinfo); + break; + + case OPS_PTAG_CT_COMPRESSED: + r=parse_compressed(®ion,pinfo); + break; + + case OPS_PTAG_CT_ONE_PASS_SIGNATURE: + r=parse_one_pass(®ion,pinfo); + break; + + case OPS_PTAG_CT_LITERAL_DATA: + r=parse_literal_data(®ion,pinfo); + break; + + case OPS_PTAG_CT_USER_ATTRIBUTE: + r=parse_user_attribute(®ion,pinfo); + break; + + case OPS_PTAG_CT_SECRET_KEY: + r=parse_secret_key(®ion,pinfo); + break; + + case OPS_PTAG_CT_SECRET_SUBKEY: + r=parse_secret_key(®ion,pinfo); + break; + + case OPS_PTAG_CT_PK_SESSION_KEY: + r=parse_pk_session_key(®ion,pinfo); + break; + + case OPS_PTAG_CT_SE_DATA: + r=parse_se_data(®ion,pinfo); + break; + + case OPS_PTAG_CT_SE_IP_DATA: + r=parse_se_ip_data(®ion,pinfo); + break; + + case OPS_PTAG_CT_MDC: + r=parse_mdc(®ion, pinfo); + break; + + default: + OPS_ERROR_1(&pinfo->errors,OPS_E_P_UNKNOWN_TAG, + "Unknown content tag 0x%x", C.ptag.content_tag); + r=0; + } + + /* Ensure that the entire packet has been consumed */ + + if(region.length != region.length_read && !region.indeterminate) + if(!consume_packet(®ion,pinfo,ops_false)) + r=-1; + + // also consume it if there's been an error? + // \todo decide what to do about an error on an + // indeterminate packet + if (r==0) + { + if (!consume_packet(®ion,pinfo,ops_false)) + r=-1; + } + + /* set pktlen */ + + *pktlen=pinfo->rinfo.alength; + + /* do callback on entire packet, if desired and there was no error */ + + if(r > 0 && pinfo->rinfo.accumulate) + { + C.packet.length=pinfo->rinfo.alength; + C.packet.raw=pinfo->rinfo.accumulated; + pinfo->rinfo.accumulated=NULL; + pinfo->rinfo.asize=0; + CBP(pinfo,OPS_PARSER_PACKET_END,&content); + } + pinfo->rinfo.alength=0; + + if(r < 0) + return -1; + + return r ? 1 : 0; + } + +/** + * \ingroup Core_ReadPackets + * + * \brief Parse packets from an input stream until EOF or error. + * + * \details Setup the necessary parsing configuration in "pinfo" before calling ops_parse(). + * + * That information includes : + * + * - a "reader" function to be used to get the data to be parsed + * + * - a "callback" function to be called when this library has identified + * a parseable object within the data + * + * - whether the calling function wants the signature subpackets returned raw, parsed or not at all. + * + * After returning, pinfo->errors holds any errors encountered while parsing. + * + * \param pinfo Parsing configuration + * \return 1 on success in all packets, 0 on error in any packet + * + * \sa CoreAPI Overview + * + * \sa ops_print_errors(), ops_parse_and_print_errors() + * + * Example code + * \code +ops_parse_cb_t* example_callback(); +void example() + { + int fd=0; + ops_parse_info_t *pinfo=NULL; + char *filename="pubring.gpg"; + + // setup pinfo to read from file with example callback + fd=ops_setup_file_read(&pinfo, filename, NULL, example_callback, ops_false); + + // specify how we handle signature subpackets + ops_parse_options(pinfo, OPS_PTAG_SS_ALL, OPS_PARSE_PARSED); + + if (!ops_parse(pinfo)) + ops_print_errors(pinfo->errors); + + ops_teardown_file_read(pinfo,fd); + } + * \endcode + */ + +int ops_parse(ops_parse_info_t *pinfo) + { + int r; + unsigned long pktlen; + + do + { + r=ops_parse_one_packet(pinfo,&pktlen); + } while (r != -1); + + return pinfo->errors ? 0 : 1; + } + +/** +\ingroup Core_ReadPackets +\brief Parse packets and print any errors + * \param pinfo Parsing configuration + * \return 1 on success in all packets, 0 on error in any packet + * \sa CoreAPI Overview + * \sa ops_parse() +*/ + +int ops_parse_and_print_errors(ops_parse_info_t *pinfo) + { + int r; + r=ops_parse(pinfo); + ops_print_errors(pinfo->errors); + return pinfo->errors ? 0 : 1; + } + +/** + * \ingroup Core_ReadPackets + * + * \brief Specifies whether one or more signature + * subpacket types should be returned parsed; or raw; or ignored. + * + * \param pinfo Pointer to previously allocated structure + * \param tag Packet tag. OPS_PTAG_SS_ALL for all SS tags; or one individual signature subpacket tag + * \param type Parse type + * \todo Make all packet types optional, not just subpackets */ +void ops_parse_options(ops_parse_info_t *pinfo, + ops_content_tag_t tag, + ops_parse_type_t type) + { + int t8,t7; + + if(tag == OPS_PTAG_SS_ALL) + { + int n; + + for(n=0 ; n < 256 ; ++n) + ops_parse_options(pinfo,OPS_PTAG_SIGNATURE_SUBPACKET_BASE+n, + type); + return; + } + + assert(tag >= OPS_PTAG_SIGNATURE_SUBPACKET_BASE + && tag <= OPS_PTAG_SIGNATURE_SUBPACKET_BASE+NTAGS-1); + t8=(tag-OPS_PTAG_SIGNATURE_SUBPACKET_BASE)/8; + t7=1 << ((tag-OPS_PTAG_SIGNATURE_SUBPACKET_BASE)&7); + switch(type) + { + case OPS_PARSE_RAW: + pinfo->ss_raw[t8] |= t7; + pinfo->ss_parsed[t8] &= ~t7; + break; + + case OPS_PARSE_PARSED: + pinfo->ss_raw[t8] &= ~t7; + pinfo->ss_parsed[t8] |= t7; + break; + + case OPS_PARSE_IGNORE: + pinfo->ss_raw[t8] &= ~t7; + pinfo->ss_parsed[t8] &= ~t7; + break; + } + } + +/** +\ingroup Core_ReadPackets +\brief Creates a new zero-ed ops_parse_info_t struct +\sa ops_parse_info_delete() +*/ +ops_parse_info_t *ops_parse_info_new(void) + { return ops_mallocz(sizeof(ops_parse_info_t)); } + +/** +\ingroup Core_ReadPackets +\brief Free ops_parse_info_t struct and its contents +\sa ops_parse_info_new() +*/ +void ops_parse_info_delete(ops_parse_info_t *pinfo) + { + ops_parse_cb_info_t *cbinfo,*next; + + for(cbinfo=pinfo->cbinfo.next ; cbinfo ; cbinfo=next) + { + next=cbinfo->next; + free(cbinfo); + } + if(pinfo->rinfo.destroyer) + pinfo->rinfo.destroyer(&pinfo->rinfo); + ops_free_errors(pinfo->errors); + if(pinfo->rinfo.accumulated) + free(pinfo->rinfo.accumulated); + free(pinfo); + } + +/** +\ingroup Core_ReadPackets +\brief Returns the parse_info's reader_info +\return Pointer to the reader_info inside the parse_info +*/ +ops_reader_info_t *ops_parse_get_rinfo(ops_parse_info_t *pinfo) + { return &pinfo->rinfo; } + +/** +\ingroup Core_ReadPackets +\brief Sets the parse_info's callback +This is used when adding the first callback in a stack of callbacks. +\sa ops_parse_cb_push() +*/ + +void ops_parse_cb_set(ops_parse_info_t *pinfo,ops_parse_cb_t *cb,void *arg) + { + pinfo->cbinfo.cb=cb; + pinfo->cbinfo.arg=arg; + pinfo->cbinfo.errors=&pinfo->errors; + } + +/** +\ingroup Core_ReadPackets +\brief Adds a further callback to a stack of callbacks +\sa ops_parse_cb_set() +*/ +void ops_parse_cb_push(ops_parse_info_t *pinfo,ops_parse_cb_t *cb,void *arg) + { + ops_parse_cb_info_t *cbinfo=malloc(sizeof *cbinfo); + + *cbinfo=pinfo->cbinfo; + pinfo->cbinfo.next=cbinfo; + ops_parse_cb_set(pinfo,cb,arg); + } + +/** +\ingroup Core_ReadPackets +\brief Returns callback's arg +*/ +void *ops_parse_cb_get_arg(ops_parse_cb_info_t *cbinfo) + { return cbinfo->arg; } + +/** +\ingroup Core_ReadPackets +\brief Returns callback's errors +*/ +void *ops_parse_cb_get_errors(ops_parse_cb_info_t *cbinfo) + { return cbinfo->errors; } + +/** +\ingroup Core_ReadPackets +\brief Calls the parse_cb_info's callback if present +\return Return value from callback, if present; else OPS_FINISHED +*/ +ops_parse_cb_return_t ops_parse_cb(const ops_parser_content_t *content, + ops_parse_cb_info_t *cbinfo) + { + if(cbinfo->cb) + return cbinfo->cb(content,cbinfo); + else + return OPS_FINISHED; + } + +/** +\ingroup Core_ReadPackets +\brief Calls the next callback in the stack +\return Return value from callback +*/ +ops_parse_cb_return_t ops_parse_stacked_cb(const ops_parser_content_t *content, + ops_parse_cb_info_t *cbinfo) + { return ops_parse_cb(content,cbinfo->next); } + +/** +\ingroup Core_ReadPackets +\brief Returns the parse_info's errors +\return parse_info's errors +*/ +ops_error_t *ops_parse_info_get_errors(ops_parse_info_t *pinfo) + { return pinfo->errors; } + +ops_crypt_t *ops_parse_get_decrypt(ops_parse_info_t *pinfo) + { + if(pinfo->decrypt.algorithm) + return &pinfo->decrypt; + return NULL; + } + +// XXX: this could be improved by sharing all hashes that are the +// same, then duping them just before checking the signature. +void ops_parse_hash_init(ops_parse_info_t *pinfo,ops_hash_algorithm_t type, + const unsigned char *keyid) + { + ops_parse_hash_info_t *hash; + + pinfo->hashes=realloc(pinfo->hashes, + (pinfo->nhashes+1)*sizeof *pinfo->hashes); + hash=&pinfo->hashes[pinfo->nhashes++]; + + ops_hash_any(&hash->hash,type); + hash->hash.init(&hash->hash); + memcpy(hash->keyid,keyid,sizeof hash->keyid); + } + +void ops_parse_hash_data(ops_parse_info_t *pinfo,const void *data, + size_t length) + { + size_t n; + + for(n=0 ; n < pinfo->nhashes ; ++n) + pinfo->hashes[n].hash.add(&pinfo->hashes[n].hash,data,length); + } + +ops_hash_t *ops_parse_hash_find(ops_parse_info_t *pinfo, + const unsigned char keyid[OPS_KEY_ID_SIZE]) + { + size_t n; + + for(n=0 ; n < pinfo->nhashes ; ++n) + if(!memcmp(pinfo->hashes[n].keyid,keyid,OPS_KEY_ID_SIZE)) + return &pinfo->hashes[n].hash; + return NULL; + } + +/* vim:set textwidth=120: */ +/* vim:set ts=8: */ diff --git a/openpgpsdk/src/packet-print.c b/openpgpsdk/src/packet-print.c new file mode 100644 index 000000000..f15c0801f --- /dev/null +++ b/openpgpsdk/src/packet-print.c @@ -0,0 +1,1839 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*! \file + \brief Standard API print functions +*/ + +#include +#include +#include "openpgpsdk/crypto.h" +#include "openpgpsdk/keyring.h" +#include "keyring_local.h" +#include "parse_local.h" +#include "openpgpsdk/packet-show.h" +#include "openpgpsdk/util.h" +#include "openpgpsdk/std_print.h" +#include "openpgpsdk/readerwriter.h" +#include "openpgpsdk/armour.h" + +static int indent=0; + +void print_bn( const char *name, + const BIGNUM *bn); +static void print_hex(const unsigned char *src, + size_t length); +static void print_hexdump(const char *name, + const unsigned char *data, + unsigned int len); +static void print_hexdump_data(const char *name, + const unsigned char *data, + unsigned int len); +static void print_indent(); +static void print_name(const char *name); +static void print_string_and_value(char *name, + const char *str, + unsigned char value); +static void print_tagname(const char *str); +static void print_time( char *name, + time_t time); +static void print_time_short(time_t time); +static void print_unsigned_int(char *name, + unsigned int val); +static void showtime(const char *name,time_t t); +static void showtime_short(time_t t); + +/** + \ingroup Core_Print + + Prints a public key in succinct detail + + \param key Ptr to public key +*/ + +void +ops_print_public_keydata(const ops_keydata_t *key) + { + printf("pub "); + + ops_show_pka(key->key.pkey.algorithm); + printf(" "); + + hexdump(key->key_id, OPS_KEY_ID_SIZE); + printf(" "); + + print_time_short(key->key.pkey.creation_time); + printf(" "); + + if (key->nuids==1) + { + // print on same line as other info + printf ("%s\n", key->uids[0].user_id); + } + else + { + // print all uids on separate line + unsigned int i; + printf("\n"); + for (i=0; inuids; i++) + { + printf("uid %s\n",key->uids[i].user_id); + } + } + } + +/** +\ingroup Core_Print +\param pkey +*/ +void +ops_print_public_key(const ops_public_key_t *pkey) + { + printf("------- PUBLIC KEY ------\n"); + print_unsigned_int("Version",pkey->version); + print_time("Creation Time", pkey->creation_time); + if(pkey->version == OPS_V3) + print_unsigned_int("Days Valid",pkey->days_valid); + + print_string_and_value("Algorithm",ops_show_pka(pkey->algorithm), + pkey->algorithm); + + switch(pkey->algorithm) + { + case OPS_PKA_DSA: + print_bn("p",pkey->key.dsa.p); + print_bn("q",pkey->key.dsa.q); + print_bn("g",pkey->key.dsa.g); + print_bn("y",pkey->key.dsa.y); + break; + + case OPS_PKA_RSA: + case OPS_PKA_RSA_ENCRYPT_ONLY: + case OPS_PKA_RSA_SIGN_ONLY: + print_bn("n",pkey->key.rsa.n); + print_bn("e",pkey->key.rsa.e); + break; + + case OPS_PKA_ELGAMAL: + case OPS_PKA_ELGAMAL_ENCRYPT_OR_SIGN: + print_bn("p",pkey->key.elgamal.p); + print_bn("g",pkey->key.elgamal.g); + print_bn("y",pkey->key.elgamal.y); + break; + + default: + assert(0); + } + + printf("------- end of PUBLIC KEY ------\n"); + } + +/** + \ingroup Core_Print + + Prints a public key in full detail + + \param key Ptr to public key +*/ + +void +ops_print_public_keydata_verbose(const ops_keydata_t *key) + { + const ops_public_key_t* pkey=&key->key.pkey; + + ops_print_public_key(pkey); + } + +/** + \ingroup Core_Print + + Prints a secret key + + \param key Ptr to public key +*/ + +void +ops_print_secret_keydata(const ops_keydata_t *key) + { + printf("sec "); + ops_show_pka(key->key.pkey.algorithm); + printf(" "); + + hexdump(key->key_id, OPS_KEY_ID_SIZE); + printf(" "); + + print_time_short(key->key.pkey.creation_time); + printf(" "); + + if (key->nuids==1) + { + // print on same line as other info + printf ("%s\n", key->uids[0].user_id); + } + else + { + // print all uids on separate line + unsigned int i; + printf("\n"); + for (i=0; inuids; i++) + { + printf("uid %s\n",key->uids[i].user_id); + } + } + } + +/* +void +ops_print_secret_key_verbose(const ops_secret_key_t* skey) + { + if(key->type == OPS_PTAG_CT_SECRET_KEY) + print_tagname("SECRET_KEY"); + else + print_tagname("ENCRYPTED_SECRET_KEY"); + ops_print_secret_key(key->type,skey); + } +*/ + +/** +\ingroup Core_Print +\param type +\param skey +*/ +void +ops_print_secret_key_verbose(const ops_content_tag_t type, const ops_secret_key_t* skey) + { + printf("------- SECRET KEY or ENCRYPTED SECRET KEY ------\n"); + if(type == OPS_PTAG_CT_SECRET_KEY) + print_tagname("SECRET_KEY"); + else + print_tagname("ENCRYPTED_SECRET_KEY"); + // ops_print_public_key(key); + printf("S2K Usage: %d\n",skey->s2k_usage); + if(skey->s2k_usage != OPS_S2KU_NONE) + { + printf("S2K Specifier: %d\n",skey->s2k_specifier); + printf("Symmetric algorithm: %d (%s)\n",skey->algorithm, + ops_show_symmetric_algorithm(skey->algorithm)); + printf("Hash algorithm: %d (%s)\n",skey->hash_algorithm, + ops_show_hash_algorithm(skey->hash_algorithm)); + if(skey->s2k_specifier != OPS_S2KS_SIMPLE) + print_hexdump("Salt",skey->salt,sizeof skey->salt); + if(skey->s2k_specifier == OPS_S2KS_ITERATED_AND_SALTED) + printf("Octet count: %d\n",skey->octet_count); + print_hexdump("IV",skey->iv,ops_block_size(skey->algorithm)); + } + + /* no more set if encrypted */ + if(type == OPS_PTAG_CT_ENCRYPTED_SECRET_KEY) + return; + + switch(skey->public_key.algorithm) + { + case OPS_PKA_RSA: + print_bn("d",skey->key.rsa.d); + print_bn("p",skey->key.rsa.p); + print_bn("q",skey->key.rsa.q); + print_bn("u",skey->key.rsa.u); + break; + + case OPS_PKA_DSA: + print_bn("x",skey->key.dsa.x); + break; + + default: + assert(0); + } + + if(skey->s2k_usage == OPS_S2KU_ENCRYPTED_AND_HASHED) + print_hexdump("Checkhash",skey->checkhash,OPS_CHECKHASH_SIZE); + else + printf("Checksum: %04x\n",skey->checksum); + + printf("------- end of SECRET KEY or ENCRYPTED SECRET KEY ------\n"); + } + +/** +\ingroup Core_Print +\param key +*/ +void +ops_print_secret_keydata_verbose(const ops_keydata_t *key) + { + const ops_secret_key_t* skey=&key->key.skey; + ops_print_public_keydata(key); + ops_print_secret_key_verbose(key->type,skey); + } + +// static functions + +static void print_unsigned_int(char *name, unsigned int val) + { + print_name(name); + printf("%d\n", val); + } + +static void print_time( char *name, time_t time) + { + print_indent(); + printf("%s: ",name); + showtime("time",time); + printf("\n"); + } + +static void print_time_short(time_t time) + { + showtime_short(time); + } + +static void print_string_and_value(char *name,const char *str, + unsigned char value) + { + print_name(name); + + printf("%s", str); + printf(" (0x%x)", value); + printf("\n"); + } + +void print_bn( const char *name, const BIGNUM *bn) + { + print_indent(); + printf("%s=",name); + if(bn) + { + BN_print_fp(stdout,bn); + putchar('\n'); + } + else + puts("(unset)"); + } + +static void print_tagname(const char *str) + { + print_indent(); + printf("%s packet\n", str); + } + +static void print_hexdump(const char *name, + const unsigned char *data, + unsigned int len) + { + print_name(name); + + printf("len=%d, data=0x", len); + print_hex(data,len); + printf("\n"); + } + +static void print_hexdump_data(const char *name, + const unsigned char *data, + unsigned int len) + { + print_name(name); + + printf("0x"); + print_hex(data,len); + printf("\n"); + } + +static void print_data(const char *name,const ops_data_t *data) + { + print_hexdump(name,data->contents,data->len); + } + + +static void print_name(const char *name) + { + print_indent(); + if(name) + printf("%s: ",name); + } + +static void print_indent() + { + int i=0; + + for(i=0 ; i < indent ; i++) + printf(" "); + } + +/* printhex is now print_hex for consistency */ +static void print_hex(const unsigned char *src,size_t length) + { + while(length--) + printf("%02X",*src++); + } + +static void showtime(const char *name,time_t t) + { + printf("%s=" TIME_T_FMT " (%.24s)",name,t,ctime(&t)); + } +static void showtime_short(time_t t) + { + struct tm* tm; + /* + const int maxbuf=512; + char buf[maxbuf+1]; + buf[maxbuf]='\0'; + // this needs to be tm struct + strftime(buf,maxbuf,"%F",&t); + printf(buf); + */ + tm=gmtime(&t); + printf ("%04d-%02d-%02d", tm->tm_year+1900, tm->tm_mon, tm->tm_mday); + } + + +static void print_packet_hex(const ops_packet_t *packet) + { + unsigned char *cur; + int i; + int rem; + int blksz=4; + + printf("\nhexdump of packet contents follows:\n"); + + + for (i=1,cur=packet->raw; cur<(packet->raw+packet->length); cur+=blksz,i++) + { + rem = packet->raw+packet->length-cur; + hexdump(cur,rem<=blksz ? rem : blksz); + printf(" "); + if (!(i%8)) + printf("\n"); + + } + + printf("\n"); + } + +static void print_escaped(const unsigned char *data,size_t length) + { + while(length-- > 0) + { + if((*data >= 0x20 && *data < 0x7f && *data != '%') || *data == '\n') + putchar(*data); + else + printf("%%%02x",*data); + ++data; + } + } + +static void print_string(const char *name,const char *str) + { + print_name(name); + print_escaped((unsigned char *)str,strlen(str)); + putchar('\n'); + } + +static void print_utf8_string(const char *name,const unsigned char *str) + { + // \todo Do this better for non-English character sets + print_string(name,(const char *)str); + } + +static void print_duration(char *name, time_t time) + { + int mins, hours, days, years; + + print_indent(); + printf("%s: ",name); + printf("duration " TIME_T_FMT " seconds",time); + + mins=time/60; + hours=mins/60; + days=hours/24; + years=days/365; + + printf(" (approx. "); + if (years) + printf("%d %s",years,years==1?"year":"years"); + else if (days) + printf("%d %s",days,days==1?"day":"days"); + else if (hours) + printf("%d %s", hours, hours==1?"hour":"hours"); + + printf(")"); + printf("\n"); + } + +static void print_boolean(const char *name, unsigned char bool) + { + print_name(name); + + if(bool) + printf("Yes"); + else + printf("No"); + printf("\n"); + } + +static void print_text_breakdown( ops_text_t *text) + { + unsigned i; + char *prefix=".. "; + + /* these were recognised */ + + for(i=0 ; iknown.used ; i++) + { + print_indent(); + printf("%s",prefix); + printf("%s\n",text->known.strings[i]); + } + + /* these were not recognised. the strings will contain the hex value + of the unrecognised value in string format - see process_octet_str() + */ + + if(text->unknown.used) + { + printf("\n"); + print_indent(); + printf("Not Recognised: "); + } + for( i=0; i < text->unknown.used; i++) + { + print_indent(); + printf("%s",prefix); + printf("%s\n",text->unknown.strings[i]); + } + + } + +static void print_headers(const ops_headers_t *headers) + { + unsigned n; + + for(n=0 ; n < headers->nheaders ; ++n) + printf("%s=%s\n",headers->headers[n].key,headers->headers[n].value); + } + +static void print_block(const char *name,const unsigned char *str, + size_t length) + { + int o=length; + + print_indent(); + printf(">>>>> %s >>>>>\n",name); + + print_indent(); + for( ; length > 0 ; --length) + { + if(*str >= 0x20 && *str < 0x7f && *str != '%') + putchar(*str); + else if(*str == '\n') + { + putchar(*str); + print_indent(); + } + else + printf("%%%02x",*str); + ++str; + } + if(o && str[-1] != '\n') + { + putchar('\n'); + print_indent(); + fputs("[no newline]",stdout); + } + else + print_indent(); + printf("<<<<< %s <<<<<\n",name); + } + +/** +\ingroup Core_Print +\param tag +\param key +*/ +void ops_print_pk_session_key(ops_content_tag_t tag, + const ops_pk_session_key_t *key) + { + if(tag == OPS_PTAG_CT_PK_SESSION_KEY) + print_tagname("PUBLIC KEY SESSION KEY"); + else + print_tagname("ENCRYPTED PUBLIC KEY SESSION KEY"); + + printf("Version: %d\n",key->version); + print_hexdump("Key ID",key->key_id,sizeof key->key_id); + printf("Algorithm: %d (%s)\n",key->algorithm, + ops_show_pka(key->algorithm)); + switch(key->algorithm) + { + case OPS_PKA_RSA: + print_bn("encrypted_m",key->parameters.rsa.encrypted_m); + break; + + case OPS_PKA_ELGAMAL: + print_bn("g_to_k",key->parameters.elgamal.g_to_k); + print_bn("encrypted_m",key->parameters.elgamal.encrypted_m); + break; + + default: + assert(0); + } + + if(tag != OPS_PTAG_CT_PK_SESSION_KEY) + return; + + printf("Symmetric algorithm: %d (%s)\n",key->symmetric_algorithm, + ops_show_symmetric_algorithm(key->symmetric_algorithm)); + print_hexdump("Key",key->key,ops_key_size(key->symmetric_algorithm)); + printf("Checksum: %04x\n",key->checksum); + } + +static void start_subpacket(unsigned type) + { + indent++; + print_indent(); + printf("-- %s (type 0x%02x)\n", + ops_show_ss_type(type), + type-OPS_PTAG_SIGNATURE_SUBPACKET_BASE); + } + +static void end_subpacket() + { + indent--; + } + +/** +\ingroup Core_Print +\param content_ +*/ +int ops_print_packet(const ops_parser_content_t *content_) + { + const ops_parser_content_union_t *content=&content_->content; + ops_text_t *text; + const char *str; + static ops_boolean_t unarmoured; + + if(unarmoured && content_->tag != OPS_PTAG_CT_UNARMOURED_TEXT) + { + unarmoured=ops_false; + puts("UNARMOURED TEXT ends"); + } + + if (content_->tag==OPS_PARSER_PTAG) + { + printf("=> OPS_PARSER_PTAG: %s\n", ops_show_packet_tag(content->ptag.content_tag)); + } + else + { + printf("=> %s\n", ops_show_packet_tag(content_->tag)); + } + + switch(content_->tag) + { + case OPS_PARSER_ERROR: + printf("parse error: %s\n",content->error.error); + break; + + case OPS_PARSER_ERRCODE: + printf("parse error: %s\n", + ops_errcode(content->errcode.errcode)); + break; + + case OPS_PARSER_PACKET_END: + print_packet_hex(&content->packet); + break; + + case OPS_PARSER_PTAG: + if(content->ptag.content_tag == OPS_PTAG_CT_PUBLIC_KEY) + { + indent=0; + printf("\n*** NEXT KEY ***\n"); + } + + printf("\n"); + print_indent(); + printf("==== ptag new_format=%d content_tag=%d length_type=%d" + " length=0x%x (%d) position=0x%x (%d)\n",content->ptag.new_format, + content->ptag.content_tag,content->ptag.length_type, + content->ptag.length,content->ptag.length, + content->ptag.position,content->ptag.position); + print_tagname(ops_show_packet_tag(content->ptag.content_tag)); + break; + + case OPS_PTAG_CT_SE_DATA_HEADER: + print_tagname("SYMMETRIC ENCRYPTED DATA"); + break; + + case OPS_PTAG_CT_SE_IP_DATA_HEADER: + print_tagname("SYMMETRIC ENCRYPTED INTEGRITY PROTECTED DATA HEADER"); + printf("Version: %d\n",content->se_ip_data_header.version); + break; + + case OPS_PTAG_CT_SE_IP_DATA_BODY: + print_tagname("SYMMETRIC ENCRYPTED INTEGRITY PROTECTED DATA BODY"); + printf(" data body length=%d\n", + content->se_data_body.length); + printf(" data="); + hexdump(content->se_data_body.data, + content->se_data_body.length); + printf("\n"); + break; + + case OPS_PTAG_CT_PUBLIC_KEY: + case OPS_PTAG_CT_PUBLIC_SUBKEY: + if (content_->tag == OPS_PTAG_CT_PUBLIC_KEY) + print_tagname("PUBLIC KEY"); + else + print_tagname("PUBLIC SUBKEY"); + ops_print_public_key(&content->public_key); + break; + + case OPS_PTAG_CT_TRUST: + print_tagname("TRUST"); + print_data("Trust",&content->trust.data); + break; + + case OPS_PTAG_CT_USER_ID: + /* XXX: how do we print UTF-8? */ + print_tagname("USER ID"); + print_utf8_string("user_id",content->user_id.user_id); + break; + + case OPS_PTAG_CT_SIGNATURE: + print_tagname("SIGNATURE"); + print_indent(); + print_unsigned_int("Signature Version", + content->signature.info.version); + if (content->signature.info.creation_time_set) + print_time("Signature Creation Time", + content->signature.info.creation_time); + + print_string_and_value("Signature Type", + ops_show_sig_type(content->signature.info.type), + content->signature.info.type); + + if(content->signature.info.signer_id_set) + print_hexdump_data("Signer ID", + content->signature.info.signer_id, + sizeof content->signature.info.signer_id); + + print_string_and_value("Public Key Algorithm", + ops_show_pka(content->signature.info.key_algorithm), + content->signature.info.key_algorithm); + print_string_and_value("Hash Algorithm", + ops_show_hash_algorithm(content->signature.info.hash_algorithm), + content->signature.info.hash_algorithm); + + print_unsigned_int("Hashed data len", content->signature.info.v4_hashed_data_length); + + print_indent(); + print_hexdump_data("hash2",&content->signature.hash2[0],2); + + switch(content->signature.info.key_algorithm) + { + case OPS_PKA_RSA: + case OPS_PKA_RSA_SIGN_ONLY: + print_bn("sig",content->signature.info.signature.rsa.sig); + break; + + case OPS_PKA_DSA: + print_bn("r",content->signature.info.signature.dsa.r); + print_bn("s",content->signature.info.signature.dsa.s); + break; + + case OPS_PKA_ELGAMAL_ENCRYPT_OR_SIGN: + print_bn("r",content->signature.info.signature.elgamal.r); + print_bn("s",content->signature.info.signature.elgamal.s); + break; + + default: + assert(0); + } + + if(content->signature.hash) + printf("data hash is set\n"); + + break; + + case OPS_PTAG_CT_COMPRESSED: + print_tagname("COMPRESSED"); + print_unsigned_int("Compressed Data Type", content->compressed.type); + break; + + case OPS_PTAG_CT_ONE_PASS_SIGNATURE: + print_tagname("ONE PASS SIGNATURE"); + + print_unsigned_int("Version",content->one_pass_signature.version); + print_string_and_value("Signature Type", + ops_show_sig_type(content->one_pass_signature.sig_type), + content->one_pass_signature.sig_type); + print_string_and_value("Hash Algorithm", + ops_show_hash_algorithm(content->one_pass_signature.hash_algorithm), + content->one_pass_signature.hash_algorithm); + print_string_and_value("Public Key Algorithm", + ops_show_pka(content->one_pass_signature.key_algorithm), + content->one_pass_signature.key_algorithm); + print_hexdump_data("Signer ID", + content->one_pass_signature.keyid, + sizeof content->one_pass_signature.keyid); + + print_unsigned_int("Nested", + content->one_pass_signature.nested); + break; + + case OPS_PTAG_CT_USER_ATTRIBUTE: + print_tagname("USER ATTRIBUTE"); + print_hexdump("User Attribute", + content->user_attribute.data.contents, + content->user_attribute.data.len); + break; + + case OPS_PTAG_RAW_SS: + assert(!content_->critical); + start_subpacket(content_->tag); + print_unsigned_int("Raw Signature Subpacket: tag", + content->ss_raw.tag-OPS_PTAG_SIGNATURE_SUBPACKET_BASE); + print_hexdump("Raw Data", + content->ss_raw.raw, + content->ss_raw.length); + break; + + case OPS_PTAG_SS_CREATION_TIME: + start_subpacket(content_->tag); + print_time("Signature Creation Time",content->ss_time.time); + end_subpacket(); + break; + + case OPS_PTAG_SS_EXPIRATION_TIME: + start_subpacket(content_->tag); + print_duration("Signature Expiration Time",content->ss_time.time); + end_subpacket(); + break; + + case OPS_PTAG_SS_KEY_EXPIRATION_TIME: + start_subpacket(content_->tag); + print_duration("Key Expiration Time", content->ss_time.time); + end_subpacket(); + break; + + case OPS_PTAG_SS_TRUST: + start_subpacket(content_->tag); + print_string("Trust Signature",""); + print_unsigned_int("Level", + content->ss_trust.level); + print_unsigned_int("Amount", + content->ss_trust.amount); + end_subpacket(); + break; + + case OPS_PTAG_SS_REVOCABLE: + start_subpacket(content_->tag); + print_boolean("Revocable",content->ss_revocable.revocable); + end_subpacket(); + break; + + case OPS_PTAG_SS_REVOCATION_KEY: + start_subpacket(content_->tag); + /* not yet tested */ + printf (" revocation key: class=0x%x", + content->ss_revocation_key.class); + if (content->ss_revocation_key.class&0x40) + printf (" (sensitive)"); + printf (", algid=0x%x", + content->ss_revocation_key.algid); + printf(", fingerprint="); + hexdump(content->ss_revocation_key.fingerprint,20); + printf("\n"); + end_subpacket(); + break; + + case OPS_PTAG_SS_ISSUER_KEY_ID: + start_subpacket(content_->tag); + print_hexdump("Issuer Key Id", + &content->ss_issuer_key_id.key_id[0], + sizeof content->ss_issuer_key_id.key_id); + end_subpacket(); + break; + + case OPS_PTAG_SS_PREFERRED_SKA: + start_subpacket(content_->tag); + print_data( "Preferred Symmetric Algorithms", + &content->ss_preferred_ska.data); + + text = ops_showall_ss_preferred_ska(content->ss_preferred_ska); + print_text_breakdown(text); + ops_text_free(text); + + end_subpacket(); + break; + + case OPS_PTAG_SS_PRIMARY_USER_ID: + start_subpacket(content_->tag); + print_boolean("Primary User ID", + content->ss_primary_user_id.primary_user_id); + end_subpacket(); + break; + + case OPS_PTAG_SS_PREFERRED_HASH: + start_subpacket(content_->tag); + print_data("Preferred Hash Algorithms", + &content->ss_preferred_hash.data); + + text = ops_showall_ss_preferred_hash(content->ss_preferred_hash); + print_text_breakdown(text); + ops_text_free(text); + end_subpacket(); + break; + + case OPS_PTAG_SS_PREFERRED_COMPRESSION: + start_subpacket(content_->tag); + print_data( "Preferred Compression Algorithms", + &content->ss_preferred_compression.data); + + text = ops_showall_ss_preferred_compression(content->ss_preferred_compression); + print_text_breakdown(text); + ops_text_free(text); + end_subpacket(); + break; + + case OPS_PTAG_SS_KEY_FLAGS: + start_subpacket(content_->tag); + print_data( "Key Flags", &content->ss_key_flags.data); + + text = ops_showall_ss_key_flags(content->ss_key_flags); + print_text_breakdown( text); + ops_text_free(text); + + end_subpacket(); + break; + + case OPS_PTAG_SS_KEY_SERVER_PREFS: + start_subpacket(content_->tag); + print_data( "Key Server Preferences", + &content->ss_key_server_prefs.data); + + text = ops_showall_ss_key_server_prefs(content->ss_key_server_prefs); + print_text_breakdown( text); + ops_text_free(text); + + end_subpacket(); + break; + + case OPS_PTAG_SS_FEATURES: + start_subpacket(content_->tag); + print_data( "Features", + &content->ss_features.data); + + text = ops_showall_ss_features(content->ss_features); + print_text_breakdown( text); + ops_text_free(text); + + end_subpacket(); + break; + + case OPS_PTAG_SS_NOTATION_DATA: + start_subpacket(content_->tag); + print_indent(); + printf("Notation Data:\n"); + + indent++; + print_data( "Flags", + &content->ss_notation_data.flags); + text = ops_showall_ss_notation_data_flags(content->ss_notation_data); + print_text_breakdown( text); + ops_text_free(text); + + /* xxx - TODO: print out UTF - rachel */ + + print_data( "Name", + &content->ss_notation_data.name); + + print_data( "Value", + &content->ss_notation_data.value); + + indent--; + end_subpacket(); + break; + + case OPS_PTAG_SS_REGEXP: + start_subpacket(content_->tag); + print_hexdump("Regular Expression", + (unsigned char *)content->ss_regexp.text, + strlen(content->ss_regexp.text)); + print_string(NULL, + content->ss_regexp.text); + end_subpacket(); + break; + + case OPS_PTAG_SS_POLICY_URI: + start_subpacket(content_->tag); + print_string("Policy URL", + content->ss_policy_url.text); + end_subpacket(); + break; + + case OPS_PTAG_SS_SIGNERS_USER_ID: + start_subpacket(content_->tag); + print_utf8_string("Signer's User ID",content->ss_signers_user_id.user_id); + end_subpacket(); + break; + + case OPS_PTAG_SS_PREFERRED_KEY_SERVER: + start_subpacket(content_->tag); + print_string("Preferred Key Server", + content->ss_preferred_key_server.text); + end_subpacket(); + break; + + case OPS_PTAG_SS_EMBEDDED_SIGNATURE: + start_subpacket(content_->tag); + end_subpacket(content_->tag); // \todo print out contents? + break; + + case OPS_PTAG_SS_USERDEFINED00: + case OPS_PTAG_SS_USERDEFINED01: + case OPS_PTAG_SS_USERDEFINED02: + case OPS_PTAG_SS_USERDEFINED03: + case OPS_PTAG_SS_USERDEFINED04: + case OPS_PTAG_SS_USERDEFINED05: + case OPS_PTAG_SS_USERDEFINED06: + case OPS_PTAG_SS_USERDEFINED07: + case OPS_PTAG_SS_USERDEFINED08: + case OPS_PTAG_SS_USERDEFINED09: + case OPS_PTAG_SS_USERDEFINED10: + start_subpacket(content_->tag); + print_hexdump("Internal or user-defined", + content->ss_userdefined.data.contents, + content->ss_userdefined.data.len); + end_subpacket(); + break; + + case OPS_PTAG_SS_RESERVED: + start_subpacket(content_->tag); + print_hexdump("Reserved", + content->ss_userdefined.data.contents, + content->ss_userdefined.data.len); + end_subpacket(); + break; + + case OPS_PTAG_SS_REVOCATION_REASON: + start_subpacket(content_->tag); + print_hexdump("Revocation Reason", + &content->ss_revocation_reason.code, + 1); + str=ops_show_ss_rr_code(content->ss_revocation_reason.code); + print_string(NULL,str); + /* xxx - todo : output text as UTF-8 string */ + end_subpacket(); + break; + + case OPS_PTAG_CT_LITERAL_DATA_HEADER: + print_tagname("LITERAL DATA HEADER"); + printf(" literal data header format=%c filename='%s'\n", + content->literal_data_header.format, + content->literal_data_header.filename); + showtime(" modification time", + content->literal_data_header.modification_time); + printf("\n"); + break; + + case OPS_PTAG_CT_LITERAL_DATA_BODY: + print_tagname("LITERAL DATA BODY"); + printf(" literal data body length=%d\n", + content->literal_data_body.length); + printf(" data="); + print_escaped(content->literal_data_body.data, + content->literal_data_body.length); + printf("\n"); + break; + + case OPS_PTAG_CT_SIGNATURE_HEADER: + print_tagname("SIGNATURE"); + print_indent(); + print_unsigned_int("Signature Version", + content->signature.info.version); + if(content->signature.info.creation_time_set) + print_time("Signature Creation Time", content->signature.info.creation_time); + + print_string_and_value("Signature Type", + ops_show_sig_type(content->signature.info.type), + content->signature.info.type); + + if(content->signature.info.signer_id_set) + print_hexdump_data("Signer ID", + content->signature.info.signer_id, + sizeof content->signature.info.signer_id); + + print_string_and_value("Public Key Algorithm", + ops_show_pka(content->signature.info.key_algorithm), + content->signature.info.key_algorithm); + print_string_and_value("Hash Algorithm", + ops_show_hash_algorithm(content->signature.info.hash_algorithm), + content->signature.info.hash_algorithm); + + break; + + case OPS_PTAG_CT_SIGNATURE_FOOTER: + print_indent(); + print_hexdump_data("hash2",&content->signature.hash2[0],2); + + switch(content->signature.info.key_algorithm) + { + case OPS_PKA_RSA: + print_bn("sig",content->signature.info.signature.rsa.sig); + break; + + case OPS_PKA_DSA: + print_bn("r",content->signature.info.signature.dsa.r); + print_bn("s",content->signature.info.signature.dsa.s); + break; + + case OPS_PKA_ELGAMAL_ENCRYPT_OR_SIGN: + print_bn("r",content->signature.info.signature.elgamal.r); + print_bn("s",content->signature.info.signature.elgamal.s); + break; + + case OPS_PKA_PRIVATE00: + case OPS_PKA_PRIVATE01: + case OPS_PKA_PRIVATE02: + case OPS_PKA_PRIVATE03: + case OPS_PKA_PRIVATE04: + case OPS_PKA_PRIVATE05: + case OPS_PKA_PRIVATE06: + case OPS_PKA_PRIVATE07: + case OPS_PKA_PRIVATE08: + case OPS_PKA_PRIVATE09: + case OPS_PKA_PRIVATE10: + print_data("Private/Experimental", + &content->signature.info.signature.unknown.data); + break; + + default: + assert(0); + } + break; + + case OPS_PARSER_CMD_GET_SK_PASSPHRASE: + print_tagname("OPS_PARSER_CMD_GET_SK_PASSPHRASE"); + /* + if(passphrase_prompt) + { + print_secret_key(OPS_PTAG_CT_ENCRYPTED_SECRET_KEY, + content->secret_key_passphrase.secret_key); + *content->secret_key_passphrase.passphrase=ops_get_passphrase(); + if(!**content->secret_key_passphrase.passphrase) + break; + return OPS_KEEP_MEMORY; + } + else + printf(">>> ASKED FOR PASSPHRASE <<<\n"); + */ + + break; + + case OPS_PTAG_CT_SECRET_KEY: + print_tagname("OPS_PTAG_CT_SECRET_KEY"); + ops_print_secret_key_verbose(content_->tag,&content->secret_key); + break; + + case OPS_PTAG_CT_ENCRYPTED_SECRET_KEY: + // print_secret_key(content_->tag,&content->secret_key); + print_tagname("OPS_PTAG_CT_ENCRYPTED_SECRET_KEY"); + ops_print_secret_key_verbose(content_->tag,&content->secret_key); + break; + + case OPS_PTAG_CT_ARMOUR_HEADER: + print_tagname("ARMOUR HEADER"); + print_string("type",content->armour_header.type); + break; + + case OPS_PTAG_CT_SIGNED_CLEARTEXT_HEADER: + print_tagname("SIGNED CLEARTEXT HEADER"); + print_headers(&content->signed_cleartext_header.headers); + break; + + case OPS_PTAG_CT_SIGNED_CLEARTEXT_BODY: + print_tagname("SIGNED CLEARTEXT BODY"); + print_block("signed cleartext",content->signed_cleartext_body.data, + content->signed_cleartext_body.length); + break; + + case OPS_PTAG_CT_SIGNED_CLEARTEXT_TRAILER: + print_tagname("SIGNED CLEARTEXT TRAILER"); + printf("hash algorithm: %d\n", + content->signed_cleartext_trailer.hash->algorithm); + printf("\n"); + break; + + case OPS_PTAG_CT_UNARMOURED_TEXT: + if(!unarmoured) + { + print_tagname("UNARMOURED TEXT"); + unarmoured=ops_true; + } + putchar('['); + print_escaped(content->unarmoured_text.data, + content->unarmoured_text.length); + putchar(']'); + break; + + case OPS_PTAG_CT_ARMOUR_TRAILER: + print_tagname("ARMOUR TRAILER"); + print_string("type",content->armour_header.type); + break; + + case OPS_PTAG_CT_PK_SESSION_KEY: + case OPS_PTAG_CT_ENCRYPTED_PK_SESSION_KEY: + ops_print_pk_session_key(content_->tag,&content->pk_session_key); + break; + + case OPS_PARSER_CMD_GET_SECRET_KEY: + ops_print_pk_session_key(OPS_PTAG_CT_ENCRYPTED_PK_SESSION_KEY, + content->get_secret_key.pk_session_key); + break; + + default: + print_tagname("UNKNOWN PACKET TYPE"); + fprintf(stderr,"ops_print_packet: unknown tag=%d (0x%x)\n",content_->tag, + content_->tag); + exit(1); + } + return 1; + } + +static ops_parse_cb_return_t cb_list_packets(const ops_parser_content_t * content_, ops_parse_cb_info_t *cbinfo) + { + OPS_USED(cbinfo); + + ops_print_packet(content_); +#ifdef XXX + if(unarmoured && content_->tag != OPS_PTAG_CT_UNARMOURED_TEXT) + { + unarmoured=ops_false; + puts("UNARMOURED TEXT ends"); + } + + switch(content_->tag) + { + case OPS_PARSER_ERROR: + printf("parse error: %s\n",content->error.error); + break; + + case OPS_PARSER_ERRCODE: + printf("parse error: %s\n", + ops_errcode(content->errcode.errcode)); + break; + + case OPS_PARSER_PACKET_END: + print_packet_hex(&content->packet); + break; + + case OPS_PARSER_PTAG: + if(content->ptag.content_tag == OPS_PTAG_CT_PUBLIC_KEY) + { + indent=0; + printf("\n*** NEXT KEY ***\n"); + } + + printf("\n"); + print_indent(); + printf("==== ptag new_format=%d content_tag=%d length_type=%d" + " length=0x%x (%d) position=0x%x (%d)\n",content->ptag.new_format, + content->ptag.content_tag,content->ptag.length_type, + content->ptag.length,content->ptag.length, + content->ptag.position,content->ptag.position); + print_tagname(ops_show_packet_tag(content->ptag.content_tag)); + break; + + case OPS_PTAG_CT_SE_DATA_HEADER: + print_tagname("SYMMETRIC ENCRYPTED DATA"); + break; + + case OPS_PTAG_CT_SE_IP_DATA_HEADER: + print_tagname("SYMMETRIC ENCRYPTED INTEGRITY PROTECTED DATA HEADER"); + printf("Version: %d\n",content->se_ip_data_header.version); + break; + + case OPS_PTAG_CT_SE_IP_DATA_BODY: + print_tagname("SYMMETRIC ENCRYPTED INTEGRITY PROTECTED DATA BODY"); + printf(" data body length=%d\n", + content->se_data_body.length); + printf(" data="); + hexdump(content->se_data_body.data, + content->se_data_body.length); + printf("\n"); + break; + + case OPS_PTAG_CT_PUBLIC_KEY: + case OPS_PTAG_CT_PUBLIC_SUBKEY: + if (content_->tag == OPS_PTAG_CT_PUBLIC_KEY) + print_tagname("PUBLIC KEY"); + else + print_tagname("PUBLIC SUBKEY"); + + ops_print_public_key(&content->public_key); + break; + + case OPS_PTAG_CT_TRUST: + print_tagname("TRUST"); + print_data("Trust",&content->trust.data); + break; + + case OPS_PTAG_CT_USER_ID: + /* XXX: how do we print UTF-8? */ + print_tagname("USER ID"); + print_utf8_string("user_id",content->user_id.user_id); + break; + + case OPS_PTAG_CT_SIGNATURE: + print_tagname("SIGNATURE"); + print_indent(); + print_unsigned_int("Signature Version", + content->signature.info.version); + if (content->signature.info.creation_time_set) + print_time("Signature Creation Time", + content->signature.info.creation_time); + + print_string_and_value("Signature Type", + ops_show_sig_type(content->signature.info.type), + content->signature.info.type); + + if(content->signature.info.signer_id_set) + print_hexdump_data("Signer ID", + content->signature.info.signer_id, + sizeof content->signature.info.signer_id); + + print_string_and_value("Public Key Algorithm", + ops_show_pka(content->signature.info.key_algorithm), + content->signature.info.key_algorithm); + print_string_and_value("Hash Algorithm", + ops_show_hash_algorithm(content->signature.info.hash_algorithm), + content->signature.info.hash_algorithm); + print_unsigned_int("Hashed data len", content->signature.info.v4_hashed_data_length); + + print_indent(); + print_hexdump_data("hash2",&content->signature.hash2[0],2); + + switch(content->signature.info.key_algorithm) + { + case OPS_PKA_RSA: + case OPS_PKA_RSA_SIGN_ONLY: + print_bn("sig",content->signature.info.signature.rsa.sig); + break; + + case OPS_PKA_DSA: + print_bn("r",content->signature.info.signature.dsa.r); + print_bn("s",content->signature.info.signature.dsa.s); + break; + + case OPS_PKA_ELGAMAL_ENCRYPT_OR_SIGN: + print_bn("r",content->signature.info.signature.elgamal.r); + print_bn("s",content->signature.info.signature.elgamal.s); + break; + + default: + assert(0); + } + + if(content->signature.hash) + printf("data hash is set\n"); + + break; + + case OPS_PTAG_CT_COMPRESSED: + print_tagname("COMPRESSED"); + print_unsigned_int("Compressed Data Type", content->compressed.type); + break; + + case OPS_PTAG_CT_ONE_PASS_SIGNATURE: + print_tagname("ONE PASS SIGNATURE"); + + print_unsigned_int("Version",content->one_pass_signature.version); + print_string_and_value("Signature Type", + ops_show_sig_type(content->one_pass_signature.sig_type), + content->one_pass_signature.sig_type); + print_string_and_value("Hash Algorithm", + ops_show_hash_algorithm(content->one_pass_signature.hash_algorithm), + content->one_pass_signature.hash_algorithm); + print_string_and_value("Public Key Algorithm", + ops_show_pka(content->one_pass_signature.key_algorithm), + content->one_pass_signature.key_algorithm); + print_hexdump_data("Signer ID", + content->one_pass_signature.keyid, + sizeof content->one_pass_signature.keyid); + + print_unsigned_int("Nested", + content->one_pass_signature.nested); + break; + + case OPS_PTAG_CT_USER_ATTRIBUTE: + print_tagname("USER ATTRIBUTE"); + print_hexdump("User Attribute", + content->user_attribute.data.contents, + content->user_attribute.data.len); + break; + + case OPS_PTAG_RAW_SS: + assert(!content_->critical); + start_subpacket(content_->tag); + print_unsigned_int("Raw Signature Subpacket: tag", + content->ss_raw.tag-OPS_PTAG_SIGNATURE_SUBPACKET_BASE); + print_hexdump("Raw Data", + content->ss_raw.raw, + content->ss_raw.length); + break; + + case OPS_PTAG_SS_CREATION_TIME: + start_subpacket(content_->tag); + print_time("Signature Creation Time",content->ss_time.time); + end_subpacket(); + break; + + case OPS_PTAG_SS_EXPIRATION_TIME: + start_subpacket(content_->tag); + print_duration("Signature Expiration Time",content->ss_time.time); + end_subpacket(); + break; + + case OPS_PTAG_SS_KEY_EXPIRATION_TIME: + start_subpacket(content_->tag); + print_duration("Key Expiration Time", content->ss_time.time); + end_subpacket(); + break; + + case OPS_PTAG_SS_TRUST: + start_subpacket(content_->tag); + print_string("Trust Signature",""); + print_unsigned_int("Level", + content->ss_trust.level); + print_unsigned_int("Amount", + content->ss_trust.amount); + end_subpacket(); + break; + + case OPS_PTAG_SS_REVOCABLE: + start_subpacket(content_->tag); + print_boolean("Revocable",content->ss_revocable.revocable); + end_subpacket(); + break; + + case OPS_PTAG_SS_REVOCATION_KEY: + start_subpacket(content_->tag); + /* not yet tested */ + printf (" revocation key: class=0x%x", + content->ss_revocation_key.class); + if (content->ss_revocation_key.class&0x40) + printf (" (sensitive)"); + printf (", algid=0x%x", + content->ss_revocation_key.algid); + printf(", fingerprint="); + hexdump(content->ss_revocation_key.fingerprint,20); + printf("\n"); + end_subpacket(); + break; + + case OPS_PTAG_SS_ISSUER_KEY_ID: + start_subpacket(content_->tag); + print_hexdump("Issuer Key Id", + &content->ss_issuer_key_id.key_id[0], + sizeof content->ss_issuer_key_id.key_id); + end_subpacket(); + break; + + case OPS_PTAG_SS_PREFERRED_SKA: + start_subpacket(content_->tag); + print_data( "Preferred Symmetric Algorithms", + &content->ss_preferred_ska.data); + + text = ops_showall_ss_preferred_ska(content->ss_preferred_ska); + print_text_breakdown(text); + ops_text_free(text); + + end_subpacket(); + break; + + case OPS_PTAG_SS_PRIMARY_USER_ID: + start_subpacket(content_->tag); + print_boolean("Primary User ID", + content->ss_primary_user_id.primary_user_id); + end_subpacket(); + break; + + case OPS_PTAG_SS_PREFERRED_HASH: + start_subpacket(content_->tag); + print_data("Preferred Hash Algorithms", + &content->ss_preferred_hash.data); + + text = ops_showall_ss_preferred_hash(content->ss_preferred_hash); + print_text_breakdown(text); + ops_text_free(text); + end_subpacket(); + break; + + case OPS_PTAG_SS_PREFERRED_COMPRESSION: + start_subpacket(content_->tag); + print_data( "Preferred Compression Algorithms", + &content->ss_preferred_compression.data); + + text = ops_showall_ss_preferred_compression(content->ss_preferred_compression); + print_text_breakdown(text); + ops_text_free(text); + end_subpacket(); + break; + + case OPS_PTAG_SS_KEY_FLAGS: + start_subpacket(content_->tag); + print_data( "Key Flags", &content->ss_key_flags.data); + + text = ops_showall_ss_key_flags(content->ss_key_flags); + print_text_breakdown( text); + ops_text_free(text); + + end_subpacket(); + break; + + case OPS_PTAG_SS_KEY_SERVER_PREFS: + start_subpacket(content_->tag); + print_data( "Key Server Preferences", + &content->ss_key_server_prefs.data); + + text = ops_showall_ss_key_server_prefs(content->ss_key_server_prefs); + print_text_breakdown( text); + ops_text_free(text); + + end_subpacket(); + break; + + case OPS_PTAG_SS_FEATURES: + start_subpacket(content_->tag); + print_data( "Features", + &content->ss_features.data); + + text = ops_showall_ss_features(content->ss_features); + print_text_breakdown( text); + ops_text_free(text); + + end_subpacket(); + break; + + case OPS_PTAG_SS_NOTATION_DATA: + start_subpacket(content_->tag); + print_indent(); + printf("Notation Data:\n"); + + indent++; + print_data( "Flags", + &content->ss_notation_data.flags); + text = ops_showall_ss_notation_data_flags(content->ss_notation_data); + print_text_breakdown( text); + ops_text_free(text); + + /* xxx - TODO: print out UTF - rachel */ + + print_data( "Name", + &content->ss_notation_data.name); + + print_data( "Value", + &content->ss_notation_data.value); + + indent--; + end_subpacket(); + break; + + case OPS_PTAG_SS_REGEXP: + start_subpacket(content_->tag); + print_hexdump("Regular Expression", + (unsigned char *)content->ss_regexp.text, + strlen(content->ss_regexp.text)); + print_string(NULL, + content->ss_regexp.text); + end_subpacket(); + break; + + case OPS_PTAG_SS_POLICY_URL: + start_subpacket(content_->tag); + print_string("Policy URL", + content->ss_policy_url.text); + end_subpacket(); + break; + + case OPS_PTAG_SS_SIGNERS_USER_ID: + start_subpacket(content_->tag); + print_utf8_string("Signer's User ID",content->ss_signers_user_id.user_id); + end_subpacket(); + break; + + case OPS_PTAG_SS_PREFERRED_KEY_SERVER: + start_subpacket(content_->tag); + print_string("Preferred Key Server", + content->ss_preferred_key_server.text); + end_subpacket(); + break; + + case OPS_PTAG_SS_USERDEFINED00: + case OPS_PTAG_SS_USERDEFINED01: + case OPS_PTAG_SS_USERDEFINED02: + case OPS_PTAG_SS_USERDEFINED03: + case OPS_PTAG_SS_USERDEFINED04: + case OPS_PTAG_SS_USERDEFINED05: + case OPS_PTAG_SS_USERDEFINED06: + case OPS_PTAG_SS_USERDEFINED07: + case OPS_PTAG_SS_USERDEFINED08: + case OPS_PTAG_SS_USERDEFINED09: + case OPS_PTAG_SS_USERDEFINED10: + start_subpacket(content_->tag); + print_hexdump("Internal or user-defined", + content->ss_userdefined.data.contents, + content->ss_userdefined.data.len); + end_subpacket(); + break; + + case OPS_PTAG_SS_RESERVED: + start_subpacket(content_->tag); + print_hexdump("Reserved", + content->ss_userdefined.data.contents, + content->ss_userdefined.data.len); + end_subpacket(); + break; + + case OPS_PTAG_SS_REVOCATION_REASON: + start_subpacket(content_->tag); + print_hexdump("Revocation Reason", + &content->ss_revocation_reason.code, + 1); + str=ops_show_ss_rr_code(content->ss_revocation_reason.code); + print_string(NULL,str); + /* xxx - todo : output text as UTF-8 string */ + end_subpacket(); + break; + + case OPS_PTAG_CT_LITERAL_DATA_HEADER: + print_tagname("LITERAL DATA HEADER"); + printf(" literal data header format=%c filename='%s'\n", + content->literal_data_header.format, + content->literal_data_header.filename); + print_time(" modification time", + content->literal_data_header.modification_time); + printf("\n"); + break; + + case OPS_PTAG_CT_LITERAL_DATA_BODY: + print_tagname("LITERAL DATA BODY"); + printf(" literal data body length=%d\n", + content->literal_data_body.length); + printf(" data="); + print_escaped(content->literal_data_body.data, + content->literal_data_body.length); + printf("\n"); + break; + + case OPS_PTAG_CT_SIGNATURE_HEADER: + print_tagname("SIGNATURE"); + print_indent(); + print_unsigned_int("Signature Version", + content->signature.info.version); + if(content->signature.info.creation_time_set) + print_time("Signature Creation Time", content->signature.info.creation_time); + + print_string_and_value("Signature Type", + ops_show_sig_type(content->signature.info.type), + content->signature.info.type); + + if(content->signature.info.signer_id_set) + print_hexdump_data("Signer ID", + content->signature.info.signer_id, + sizeof content->signature.info.signer_id); + + print_string_and_value("Public Key Algorithm", + ops_show_pka(content->signature.info.key_algorithm), + content->signature.info.key_algorithm); + print_string_and_value("Hash Algorithm", + ops_show_hash_algorithm(content->signature.info.hash_algorithm), + content->signature.info.hash_algorithm); + + break; + + case OPS_PTAG_CT_SIGNATURE_FOOTER: + print_indent(); + print_hexdump_data("hash2",&content->signature.hash2[0],2); + + switch(content->signature.info.key_algorithm) + { + case OPS_PKA_RSA: + print_bn("sig",content->signature.info.signature.rsa.sig); + break; + + case OPS_PKA_DSA: + print_bn("r",content->signature.info.signature.dsa.r); + print_bn("s",content->signature.info.signature.dsa.s); + break; + + case OPS_PKA_ELGAMAL_ENCRYPT_OR_SIGN: + print_bn("r",content->signature.info.signature.elgamal.r); + print_bn("s",content->signature.info.signature.elgamal.s); + break; + + case OPS_PKA_PRIVATE00: + case OPS_PKA_PRIVATE01: + case OPS_PKA_PRIVATE02: + case OPS_PKA_PRIVATE03: + case OPS_PKA_PRIVATE04: + case OPS_PKA_PRIVATE05: + case OPS_PKA_PRIVATE06: + case OPS_PKA_PRIVATE07: + case OPS_PKA_PRIVATE08: + case OPS_PKA_PRIVATE09: + case OPS_PKA_PRIVATE10: + print_data("Private/Experimental", + &content->signature.info.signature.unknown.data); + break; + + default: + assert(0); + } + break; + + case OPS_PARSER_CMD_GET_SK_PASSPHRASE: +#ifdef XXX + if(passphrase_prompt) + { + ops_print_secret_key(OPS_PTAG_CT_ENCRYPTED_SECRET_KEY, + content->secret_key_passphrase.secret_key); + *content->secret_key_passphrase.passphrase=ops_get_passphrase(); + if(!**content->secret_key_passphrase.passphrase) + break; + return OPS_KEEP_MEMORY; + } + else + printf(">>> ASKED FOR PASSPHRASE <<<\n"); +#else + if (cbinfo->cryptinfo.cb_get_passphrase) + return cbinfo->cryptinfo.cb_get_passphrase(content_,cbinfo); +#endif /*XXX*/ + break; + + case OPS_PTAG_CT_SECRET_KEY: + case OPS_PTAG_CT_ENCRYPTED_SECRET_KEY: + ops_print_secret_key_verbose(content_->tag,&content->secret_key); + break; + + case OPS_PTAG_CT_ARMOUR_HEADER: + print_tagname("ARMOUR HEADER"); + print_string("type",content->armour_header.type); + break; + + case OPS_PTAG_CT_SIGNED_CLEARTEXT_HEADER: + print_tagname("SIGNED CLEARTEXT HEADER"); + print_headers(&content->signed_cleartext_header.headers); + break; + + case OPS_PTAG_CT_SIGNED_CLEARTEXT_BODY: + print_tagname("SIGNED CLEARTEXT BODY"); + print_block("signed cleartext",content->signed_cleartext_body.data, + content->signed_cleartext_body.length); + break; + + case OPS_PTAG_CT_SIGNED_CLEARTEXT_TRAILER: + print_tagname("SIGNED CLEARTEXT TRAILER"); + printf("hash algorithm: %d\n", + content->signed_cleartext_trailer.hash->algorithm); + printf("\n"); + break; + + case OPS_PTAG_CT_UNARMOURED_TEXT: + if(!unarmoured) + { + print_tagname("UNARMOURED TEXT"); + unarmoured=ops_true; + } + putchar('['); + print_escaped(content->unarmoured_text.data, + content->unarmoured_text.length); + putchar(']'); + break; + + case OPS_PTAG_CT_ARMOUR_TRAILER: + print_tagname("ARMOUR TRAILER"); + print_string("type",content->armour_header.type); + break; + + case OPS_PTAG_CT_PK_SESSION_KEY: + case OPS_PTAG_CT_ENCRYPTED_PK_SESSION_KEY: + ops_print_pk_session_key(content_->tag,&content->pk_session_key); + break; + + case OPS_PARSER_CMD_GET_SECRET_KEY: + ops_print_pk_session_key(OPS_PTAG_CT_ENCRYPTED_PK_SESSION_KEY, + content->get_secret_key.pk_session_key); + +#ifdef XXX + decrypter=ops_keyring_find_key_by_id(&keyring, + content->get_secret_key.pk_session_key->key_id); + if(!decrypter || !ops_key_is_secret(decrypter)) + break; + + puts("[Decryption key found in keyring]"); + + secret=ops_get_secret_key_from_data(decrypter); + while(!secret) + { + /* then it must be encrypted */ + char *phrase=ops_get_passphrase(); + secret=ops_decrypt_secret_key_from_data(decrypter,phrase); + free(phrase); + } + + *content->get_secret_key.secret_key=secret; +#else + return callback_cmd_get_secret_key(content_,cbinfo); +#endif /*XXX*/ + break; + + default: + print_tagname("UNKNOWN PACKET TYPE"); + fprintf(stderr,"packet-dump: unknown tag=%d (0x%x)\n",content_->tag, + content_->tag); + exit(1); + } +#endif /*XXX*/ + return OPS_RELEASE_MEMORY; + } + +/** +\ingroup Core_Print +\param filename +\param armour +\param keyring +\param cb_get_passphrase +*/ +void ops_list_packets(char* filename, ops_boolean_t armour, ops_keyring_t* keyring, ops_parse_cb_t* cb_get_passphrase) + { + int fd=0; + ops_parse_info_t *pinfo=NULL; + const ops_boolean_t accumulate=ops_true; + + fd=ops_setup_file_read(&pinfo, filename, NULL, cb_list_packets, accumulate); + ops_parse_options(pinfo,OPS_PTAG_SS_ALL,OPS_PARSE_PARSED); + pinfo->cryptinfo.keyring=keyring; + pinfo->cryptinfo.cb_get_passphrase=cb_get_passphrase; + + if(armour) + ops_reader_push_dearmour(pinfo); + + ops_parse_and_print_errors(pinfo); + + ops_teardown_file_read(pinfo,fd); + } diff --git a/openpgpsdk/src/packet-show.c b/openpgpsdk/src/packet-show.c new file mode 100644 index 000000000..a0eb6da22 --- /dev/null +++ b/openpgpsdk/src/packet-show.c @@ -0,0 +1,854 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + * + * Creates printable text strings from packet contents + * + */ + +#include + +#include +#include + +#include +#include + +#include + +/* + * Arrays of value->text maps + */ + +static ops_map_t packet_tag_map[] = + { + { OPS_PTAG_CT_RESERVED, "Reserved" }, + { OPS_PTAG_CT_PK_SESSION_KEY, "Public-Key Encrypted Session Key" }, + { OPS_PTAG_CT_SIGNATURE, "Signature" }, + { OPS_PTAG_CT_SK_SESSION_KEY, "Symmetric-Key Encrypted Session Key" }, + { OPS_PTAG_CT_ONE_PASS_SIGNATURE, "One-Pass Signature" }, + { OPS_PTAG_CT_SECRET_KEY, "Secret Key" }, + { OPS_PTAG_CT_PUBLIC_KEY, "Public Key" }, + { OPS_PTAG_CT_SECRET_SUBKEY, "Secret Subkey" }, + { OPS_PTAG_CT_COMPRESSED, "Compressed Data" }, + { OPS_PTAG_CT_SE_DATA, "Symmetrically Encrypted Data" }, + { OPS_PTAG_CT_MARKER, "Marker" }, + { OPS_PTAG_CT_LITERAL_DATA, "Literal Data" }, + { OPS_PTAG_CT_TRUST, "Trust" }, + { OPS_PTAG_CT_USER_ID, "User ID" }, + { OPS_PTAG_CT_PUBLIC_SUBKEY, "Public Subkey" }, + { OPS_PTAG_CT_RESERVED2, "reserved" }, + { OPS_PTAG_CT_RESERVED3, "reserved" }, + { OPS_PTAG_CT_USER_ATTRIBUTE, "User Attribute" }, + { OPS_PTAG_CT_SE_IP_DATA, "Sym. Encrypted and Integrity Protected Data" }, + { OPS_PTAG_CT_MDC, "Modification Detection Code" }, + { OPS_PARSER_PTAG, "OPS_PARSER_PTAG" }, + { OPS_PTAG_RAW_SS, "OPS_PTAG_RAW_SS" }, + { OPS_PTAG_SS_ALL, "OPS_PTAG_SS_ALL" }, + { OPS_PARSER_PACKET_END, "OPS_PARSER_PACKET_END" }, + { OPS_PTAG_SIGNATURE_SUBPACKET_BASE, "OPS_PTAG_SIGNATURE_SUBPACKET_BASE" }, + + { OPS_PTAG_SS_CREATION_TIME, "SS: Signature Creation Time" }, + { OPS_PTAG_SS_EXPIRATION_TIME, "SS: Signature Expiration Time" }, + { OPS_PTAG_SS_EXPORTABLE_CERTIFICATION, "SS: Exportable Certification" }, + { OPS_PTAG_SS_TRUST, "SS: Trust Signature" }, + { OPS_PTAG_SS_REGEXP, "SS: Regular Expression" }, + { OPS_PTAG_SS_REVOCABLE, "SS: Revocable" }, + { OPS_PTAG_SS_KEY_EXPIRATION_TIME, "SS: Key Expiration Time" }, + { OPS_PTAG_SS_RESERVED, "SS: Reserved" }, + { OPS_PTAG_SS_PREFERRED_SKA, "SS: Preferred Secret Key Algorithm" }, + { OPS_PTAG_SS_REVOCATION_KEY, "SS: Revocation Key" }, + { OPS_PTAG_SS_ISSUER_KEY_ID, "SS: Issuer Key Id" }, + { OPS_PTAG_SS_NOTATION_DATA, "SS: Notation Data" }, + { OPS_PTAG_SS_PREFERRED_HASH, "SS: Preferred Hash Algorithm" }, + { OPS_PTAG_SS_PREFERRED_COMPRESSION,"SS: Preferred Compression Algorithm" }, + { OPS_PTAG_SS_KEY_SERVER_PREFS, "SS: Key Server Preferences" }, + { OPS_PTAG_SS_PREFERRED_COMPRESSION,"SS: Preferred Key Server" }, + { OPS_PTAG_SS_PRIMARY_USER_ID, "SS: Primary User ID" }, + { OPS_PTAG_SS_POLICY_URI, "SS: Policy URI" }, + { OPS_PTAG_SS_KEY_FLAGS, "SS: Key Flags" }, + { OPS_PTAG_SS_SIGNERS_USER_ID, "SS: Signer's User ID" }, + { OPS_PTAG_SS_REVOCATION_REASON, "SS: Reason for Revocation" }, + { OPS_PTAG_SS_FEATURES, "SS: Features" }, + { OPS_PTAG_SS_SIGNATURE_TARGET, "SS: Signature Target" }, + { OPS_PTAG_SS_EMBEDDED_SIGNATURE, "SS: Embedded Signature" }, + + { OPS_PTAG_CT_LITERAL_DATA_HEADER, "CT: Literal Data Header" }, + { OPS_PTAG_CT_LITERAL_DATA_BODY, "CT: Literal Data Body" }, + { OPS_PTAG_CT_SIGNATURE_HEADER, "CT: Signature Header" }, + { OPS_PTAG_CT_SIGNATURE_FOOTER, "CT: Signature Footer" }, + { OPS_PTAG_CT_ARMOUR_HEADER, "CT: Armour Header" }, + { OPS_PTAG_CT_ARMOUR_TRAILER, "CT: Armour Trailer" }, + { OPS_PTAG_CT_SIGNED_CLEARTEXT_HEADER, "CT: Signed Cleartext Header" }, + { OPS_PTAG_CT_SIGNED_CLEARTEXT_BODY, "CT: Signed Cleartext Body" }, + { OPS_PTAG_CT_SIGNED_CLEARTEXT_TRAILER, "CT: Signed Cleartext Trailer" }, + { OPS_PTAG_CT_UNARMOURED_TEXT, "CT: Unarmoured Text" }, + { OPS_PTAG_CT_ENCRYPTED_SECRET_KEY, "CT: Encrypted Secret Key" }, + { OPS_PTAG_CT_SE_DATA_HEADER, "CT: Sym Encrypted Data Header" }, + { OPS_PTAG_CT_SE_DATA_BODY, "CT: Sym Encrypted Data Body" }, + { OPS_PTAG_CT_SE_IP_DATA_HEADER, "CT: Sym Encrypted IP Data Header" }, + { OPS_PTAG_CT_SE_IP_DATA_BODY, "CT: Sym Encrypted IP Data Body" }, + { OPS_PTAG_CT_ENCRYPTED_PK_SESSION_KEY, "CT: Encrypted PK Session Key" }, + { OPS_PARSER_CMD_GET_SK_PASSPHRASE, "CMD: Get Secret Key Passphrase" }, + { OPS_PARSER_CMD_GET_SECRET_KEY, "CMD: Get Secret Key" }, + { OPS_PARSER_ERROR, "OPS_PARSER_ERROR" }, + { OPS_PARSER_ERRCODE, "OPS_PARSER_ERRCODE" }, + + { 0x00, NULL }, /* this is the end-of-array marker */ + }; +typedef ops_map_t packet_tag_map_t; + +static ops_map_t ss_type_map[] = + { + { OPS_PTAG_SS_CREATION_TIME, "Signature Creation Time" }, + { OPS_PTAG_SS_EXPIRATION_TIME, "Signature Expiration Time" }, + { OPS_PTAG_SS_TRUST, "Trust Signature" }, + { OPS_PTAG_SS_REGEXP, "Regular Expression" }, + { OPS_PTAG_SS_REVOCABLE, "Revocable" }, + { OPS_PTAG_SS_KEY_EXPIRATION_TIME, "Key Expiration Time" }, + { OPS_PTAG_SS_PREFERRED_SKA, "Preferred Symmetric Algorithms" }, + { OPS_PTAG_SS_REVOCATION_KEY, "Revocation Key" }, + { OPS_PTAG_SS_ISSUER_KEY_ID, "Issuer key ID" }, + { OPS_PTAG_SS_NOTATION_DATA, "Notation Data" }, + { OPS_PTAG_SS_PREFERRED_HASH, "Preferred Hash Algorithms" }, + { OPS_PTAG_SS_PREFERRED_COMPRESSION,"Preferred Compression Algorithms" }, + { OPS_PTAG_SS_KEY_SERVER_PREFS, "Key Server Preferences" }, + { OPS_PTAG_SS_PREFERRED_KEY_SERVER, "Preferred Key Server" }, + { OPS_PTAG_SS_PRIMARY_USER_ID, "Primary User ID" }, + { OPS_PTAG_SS_POLICY_URI, "Policy URI" }, + { OPS_PTAG_SS_KEY_FLAGS, "Key Flags" }, + { OPS_PTAG_SS_REVOCATION_REASON, "Reason for Revocation" }, + { OPS_PTAG_SS_FEATURES, "Features" }, + { 0x00, NULL }, /* this is the end-of-array marker */ + }; +typedef ops_map_t ss_type_map_t; + + +static ops_map_t ss_rr_code_map[] = + { + { 0x00, "No reason specified" }, + { 0x01, "Key is superseded" }, + { 0x02, "Key material has been compromised" }, + { 0x03, "Key is retired and no longer used" }, + { 0x20, "User ID information is no longer valid" }, + { 0x00, NULL }, /* this is the end-of-array marker */ + }; +typedef ops_map_t ss_rr_code_map_t; + +static ops_map_t sig_type_map[] = + { + { OPS_SIG_BINARY, "Signature of a binary document" }, + { OPS_SIG_TEXT, "Signature of a canonical text document" }, + { OPS_SIG_STANDALONE, "Standalone signature" }, + { OPS_CERT_GENERIC, "Generic certification of a User ID and Public Key packet" }, + { OPS_CERT_PERSONA, "Persona certification of a User ID and Public Key packet" }, + { OPS_CERT_CASUAL, "Casual certification of a User ID and Public Key packet" }, + { OPS_CERT_POSITIVE, "Positive certification of a User ID and Public Key packet" }, + { OPS_SIG_SUBKEY, "Subkey Binding Signature" }, + { OPS_SIG_PRIMARY, "Primary Key Binding Signature" }, + { OPS_SIG_DIRECT, "Signature directly on a key" }, + { OPS_SIG_REV_KEY, "Key revocation signature" }, + { OPS_SIG_REV_SUBKEY, "Subkey revocation signature" }, + { OPS_SIG_REV_CERT, "Certification revocation signature" }, + { OPS_SIG_TIMESTAMP, "Timestamp signature" }, + { OPS_SIG_3RD_PARTY, "Third-Party Confirmation signature" }, + { 0x00, NULL }, /* this is the end-of-array marker */ + }; +typedef ops_map_t sig_type_map_t; + +static ops_map_t public_key_algorithm_map[] = + { + { OPS_PKA_RSA, "RSA (Encrypt or Sign)" }, + { OPS_PKA_RSA_ENCRYPT_ONLY, "RSA Encrypt-Only" }, + { OPS_PKA_RSA_SIGN_ONLY, "RSA Sign-Only" }, + { OPS_PKA_ELGAMAL, "Elgamal (Encrypt-Only)" }, + { OPS_PKA_DSA, "DSA" }, + { OPS_PKA_RESERVED_ELLIPTIC_CURVE, "Reserved for Elliptic Curve" }, + { OPS_PKA_RESERVED_ECDSA, "Reserved for ECDSA" }, + { OPS_PKA_ELGAMAL_ENCRYPT_OR_SIGN, "Reserved (formerly Elgamal Encrypt or Sign" }, + { OPS_PKA_RESERVED_DH, "Reserved for Diffie-Hellman (X9.42)" }, + { OPS_PKA_PRIVATE00, "Private/Experimental" }, + { OPS_PKA_PRIVATE01, "Private/Experimental" }, + { OPS_PKA_PRIVATE02, "Private/Experimental" }, + { OPS_PKA_PRIVATE03, "Private/Experimental" }, + { OPS_PKA_PRIVATE04, "Private/Experimental" }, + { OPS_PKA_PRIVATE05, "Private/Experimental" }, + { OPS_PKA_PRIVATE06, "Private/Experimental" }, + { OPS_PKA_PRIVATE07, "Private/Experimental" }, + { OPS_PKA_PRIVATE08, "Private/Experimental" }, + { OPS_PKA_PRIVATE09, "Private/Experimental" }, + { OPS_PKA_PRIVATE10, "Private/Experimental" }, + { 0x00, NULL }, /* this is the end-of-array marker */ + }; +typedef ops_map_t public_key_algorithm_map_t; + +static ops_map_t symmetric_algorithm_map[] = + { + { OPS_SA_PLAINTEXT, "Plaintext or unencrypted data" }, + { OPS_SA_IDEA, "IDEA" }, + { OPS_SA_TRIPLEDES, "TripleDES" }, + { OPS_SA_CAST5, "CAST5" }, + { OPS_SA_BLOWFISH, "Blowfish" }, + { OPS_SA_AES_128, "AES (128-bit key)" }, + { OPS_SA_AES_192, "AES (192-bit key)" }, + { OPS_SA_AES_256, "AES (256-bit key)" }, + { OPS_SA_TWOFISH, "Twofish(256-bit key)" }, + { 0x00, NULL }, /* this is the end-of-array marker */ + }; + +static ops_map_t hash_algorithm_map[] = + { + { OPS_HASH_MD5, "MD5" }, + { OPS_HASH_SHA1, "SHA1" }, + { OPS_HASH_RIPEMD, "RIPEMD160" }, + { OPS_HASH_SHA256, "SHA256" }, + { OPS_HASH_SHA384, "SHA384" }, + { OPS_HASH_SHA512, "SHA512" }, + { OPS_HASH_SHA224, "SHA224" }, + { 0x00, NULL }, /* this is the end-of-array marker */ + }; + +static ops_map_t compression_algorithm_map[] = + { + { OPS_C_NONE, "Uncompressed" }, + { OPS_C_ZIP, "ZIP(RFC1951)" }, + { OPS_C_ZLIB, "ZLIB(RFC1950)" }, + { OPS_C_BZIP2, "Bzip2(BZ2)" }, + { 0x00, NULL }, /* this is the end-of-array marker */ + }; + +static ops_bit_map_t ss_notation_data_map_byte0[] = + { + { 0x80, "Human-readable" }, + { 0x00, NULL }, + }; + +static ops_bit_map_t *ss_notation_data_map[] = + { + ss_notation_data_map_byte0, + }; + +static ops_bit_map_t ss_feature_map_byte0[] = + { + { 0x01, "Modification Detection" }, + { 0x00, NULL }, + }; + +static ops_bit_map_t *ss_feature_map[] = + { + ss_feature_map_byte0, + }; + +static ops_bit_map_t ss_key_flags_map[] = + { + { 0x01, "May be used to certify other keys" }, + { 0x02, "May be used to sign data" }, + { 0x04, "May be used to encrypt communications" }, + { 0x08, "May be used to encrypt storage" }, + { 0x10, "Private component may have been split by a secret-sharing mechanism"}, + { 0x80, "Private component may be in possession of more than one person"}, + { 0x00, NULL }, + }; + +static ops_bit_map_t ss_key_server_prefs_map[] = + { + { 0x80, "Key holder requests that this key only be modified or updated by the key holder or an administrator of the key server" }, + { 0x00, NULL }, + }; + +#include + +/* + * Private functions + */ + +static void list_init(ops_list_t *list) + { + list->size=0; + list->used=0; + list->strings=NULL; + } + +static void list_free_strings(ops_list_t *list) + { + unsigned i; + + for(i=0; i < list->used ; i++) + { + free(list->strings[i]); + list->strings[i]=NULL; + } + } + +static void list_free(ops_list_t *list) + { + if (list->strings) + free(list->strings); + list_init(list); + } + +static unsigned int list_resize(ops_list_t *list) + { + /* We only resize in one direction - upwards. + Algorithm used : double the current size then add 1 + */ + + int newsize=0; + + newsize=list->size*2 + 1; + list->strings=realloc(list->strings,newsize*sizeof(char *)); + if (list->strings) + { + list->size=newsize; + return 1; + } + else + { + /* xxx - realloc failed. error message? - rachel */ + return 0; + } + } + +static unsigned int add_str(ops_list_t *list,char *str) + { + if (list->size==list->used) + if (!list_resize(list)) + return 0; + + list->strings[list->used]=str; + list->used++; + return 1; + } + +static char *str_from_bitfield_or_null(unsigned char octet, ops_bit_map_t *map) + { + ops_bit_map_t *row; + + for ( row=map; row->string != NULL; row++ ) + if (row->mask == octet) + return row->string; + + return NULL; + } + +static char *str_from_bitfield(unsigned char octet, ops_bit_map_t *map) + { + char *str; + str=str_from_bitfield_or_null(octet,map); + if (str) + return str; + else + return "Unknown"; + } + +/*! generic function to initialise ops_text_t structure */ +void ops_text_init(ops_text_t *text) + { + list_init(&text->known); + list_init(&text->unknown); + } + +/** + * \ingroup Core_Print + * + * ops_text_free() frees the memory used by an ops_text_t structure + * + * \param text Pointer to a previously allocated structure. This structure and its contents will be freed. + */ +void ops_text_free(ops_text_t *text) + { + /* Strings in "known" array will be constants, so don't free them */ + list_free(&text->known); + + /* Strings in "unknown" array will be dynamically allocated, so do free them */ + list_free_strings(&text->unknown); + list_free(&text->unknown); + + /* finally, free the text structure itself */ + free(text); + } + +// XXX: should this (and many others) be ops_boolean_t? +/*! generic function which adds text derived from single octet map to text */ +static unsigned int add_str_from_octet_map(ops_text_t *text,char *str, + unsigned char octet) + { + if (str && !add_str(&text->known,str)) + { + /* value recognised, but there was a problem adding it to the list */ + /* XXX - should print out error msg here, Ben? - rachel */ + return 0; + } + else if (!str) + { + /* value not recognised and there was a problem adding it to the unknown list */ + unsigned len=2+2+1; /* 2 for "0x", 2 for single octet in hex format, 1 for NULL */ + str=malloc(len); + snprintf(str,len,"0x%x",octet); + if (!add_str(&text->unknown,str)) + return 0; + } + return 1; + } + +/*! generic function which adds text derived from single bit map to text */ +static unsigned int add_str_from_bit_map(ops_text_t *text, char *str, unsigned char bit) + { + char *fmt_unknown="Unknown bit(0x%x)"; + + if (str && !add_str(&text->known,str)) + { + /* value recognised, but there was a problem adding it to the list */ + /* XXX - should print out error msg here, Ben? - rachel */ + return 0; + } + else if (!str) + { + /* value not recognised and there was a problem adding it to the unknown list */ + /* 2 chars of the string are the format definition, + this will be replaced in the output by 2 chars of hex, + so the length will be correct */ + unsigned len=strlen(fmt_unknown)+1; + str=malloc(len); + + snprintf(str,len,fmt_unknown,bit); + if (!add_str(&text->unknown,str)) + return 0; + } + return 1; + } + +/** + * Produce a structure containing human-readable textstrings + * representing the recognised and unrecognised contents + * of this byte array. text_fn() will be called on each octet in turn. + * Each octet will generate one string representing the whole byte. + * + */ + +static ops_text_t *text_from_bytemapped_octets(ops_data_t *data, + const char *(*text_fn)(unsigned char octet)) + { + + ops_text_t *text=NULL; + const char *str; + unsigned i; + + /*! allocate and initialise ops_text_t structure to store derived strings */ + text=malloc(sizeof(ops_text_t)); + if (!text) + return NULL; + + ops_text_init(text); + + /*! for each octet in field ... */ + for(i=0 ; i < data->len ; i++) + { + /*! derive string from octet */ + str=(*text_fn)(data->contents[i]); + + /*! and add to text */ + if (!add_str_from_octet_map(text,strdup(str),data->contents[i])) + { + ops_text_free(text); + return NULL; + } + + } + /*! All values have been added to either the known or the unknown list */ + /*! Return text */ + return text; + } + +/** + * Produce a structure containing human-readable textstrings + * representing the recognised and unrecognised contents + * of this byte array, derived from each bit of each octet. + * + */ +static ops_text_t *showall_octets_bits(ops_data_t *data,ops_bit_map_t **map, + size_t nmap) + { + ops_text_t *text=NULL; + char *str; + unsigned i; + int j=0; + unsigned char mask, bit; + + /*! allocate and initialise ops_text_t structure to store derived strings */ + text=malloc(sizeof(ops_text_t)); + if (!text) + return NULL; + + ops_text_init(text); + + /*! for each octet in field ... */ + for(i=0 ; i < data->len ; i++) + { + /*! for each bit in octet ... */ + for (j=0, mask=0x80; j<8; j++, mask = mask>>1 ) + { + bit = data->contents[i]&mask; + if (bit) + { + if(i >= nmap) + str="Unknown"; + else + str=str_from_bitfield ( bit, map[i] ); + if (!add_str_from_bit_map( text, str, bit)) + { + ops_text_free(text); + return NULL; + } + } + } + } + return text; + } + +/* + * Public Functions + */ + +/** + * \ingroup Core_Print + * returns description of the Packet Tag + * \param packet_tag + * \return string or "Unknown" +*/ +const char *ops_show_packet_tag(ops_packet_tag_t packet_tag) + { + char *rtn=NULL; + rtn=show_packet_tag(packet_tag,packet_tag_map); + + if (!rtn) + rtn="Unknown Tag"; + + return rtn; + } + +/** + * \ingroup Core_Print + * + * returns description of the Signature Sub-Packet type + * \param ss_type Signature Sub-Packet type + * \return string or "Unknown" + */ +const char *ops_show_ss_type(ops_ss_type_t ss_type) + { + return show_ss_type(ss_type,ss_type_map); + } + +/** + * \ingroup Core_Print + * + * returns description of the Revocation Reason code + * \param ss_rr_code Revocation Reason code + * \return string or "Unknown" + */ +const char *ops_show_ss_rr_code(ops_ss_rr_code_t ss_rr_code) + { + return show_ss_rr_code(ss_rr_code,ss_rr_code_map); + } + +/** + * \ingroup Core_Print + * + * returns description of the given Signature type + * \param sig_type Signature type + * \return string or "Unknown" + */ +const char *ops_show_sig_type(ops_sig_type_t sig_type) + { + return show_sig_type(sig_type, sig_type_map); + } + +/** + * \ingroup Core_Print + * + * returns description of the given Public Key Algorithm + * \param pka Public Key Algorithm type + * \return string or "Unknown" + */ +const char *ops_show_pka(ops_public_key_algorithm_t pka) + { + return show_pka(pka, public_key_algorithm_map); + } + +/** + * \ingroup Core_Print + * returns description of the Preferred Compression + * \param octet Preferred Compression + * \return string or "Unknown" +*/ +const char *ops_show_ss_preferred_compression(unsigned char octet) + { + return ops_str_from_map(octet,compression_algorithm_map); + } + +/** + * \ingroup Core_Print + * + * returns set of descriptions of the given Preferred Compression Algorithms + * \param ss_preferred_compression Array of Preferred Compression Algorithms + * \return NULL if cannot allocate memory or other error + * \return pointer to structure, if no error + */ +ops_text_t *ops_showall_ss_preferred_compression(ops_ss_preferred_compression_t ss_preferred_compression) + { + return text_from_bytemapped_octets(&ss_preferred_compression.data, + &ops_show_ss_preferred_compression); + } + + +/** + * \ingroup Core_Print + * + * returns description of the Hash Algorithm type + * \param hash Hash Algorithm type + * \return string or "Unknown" + */ +const char *ops_show_hash_algorithm(unsigned char hash) + { + return show_hash_algorithm(hash); + } + +/** + * \ingroup Core_Print + * + * returns set of descriptions of the given Preferred Hash Algorithms + * \param ss_preferred_hash Array of Preferred Hash Algorithms + * \return NULL if cannot allocate memory or other error + * \return pointer to structure, if no error + */ +ops_text_t *ops_showall_ss_preferred_hash(ops_ss_preferred_hash_t ss_preferred_hash) + { + return text_from_bytemapped_octets(&ss_preferred_hash.data, + &ops_show_hash_algorithm); + } + +const char *ops_show_symmetric_algorithm(unsigned char hash) + { + return show_symmetric_algorithm(hash); + } + +/** + * \ingroup Core_Print + * returns description of the given Preferred Symmetric Key Algorithm + * \param octet + * \return string or "Unknown" +*/ +const char *ops_show_ss_preferred_ska(unsigned char octet) + { + return ops_str_from_map(octet,symmetric_algorithm_map); + } + +/** + * \ingroup Core_Print + * + * returns set of descriptions of the given Preferred Symmetric Key Algorithms + * \param ss_preferred_ska Array of Preferred Symmetric Key Algorithms + * \return NULL if cannot allocate memory or other error + * \return pointer to structure, if no error + */ +ops_text_t *ops_showall_ss_preferred_ska(ops_ss_preferred_ska_t ss_preferred_ska) + { + return text_from_bytemapped_octets(&ss_preferred_ska.data, + &ops_show_ss_preferred_ska); + } + +/** + * \ingroup Core_Print + * returns description of one SS Feature + * \param octet + * \return string or "Unknown" +*/ +static char *ops_show_ss_feature(unsigned char octet,unsigned offset) + { + if(offset >= OPS_ARRAY_SIZE(ss_feature_map)) + return "Unknown"; + return str_from_bitfield(octet,ss_feature_map[offset]); + } + +/** + * \ingroup Core_Print + * + * returns set of descriptions of the given SS Features + * \param ss_features Signature Sub-Packet Features + * \return NULL if cannot allocate memory or other error + * \return pointer to structure, if no error + */ +/* XXX: shouldn't this use show_all_octets_bits? */ +ops_text_t *ops_showall_ss_features(ops_ss_features_t ss_features) + { + ops_text_t *text=NULL; + char *str; + unsigned i; + int j=0; + unsigned char mask, bit; + + text=malloc(sizeof(ops_text_t)); + if (!text) + return NULL; + + ops_text_init(text); + + for(i=0 ; i < ss_features.data.len ; i++) + { + for (j=0, mask=0x80; j<8; j++, mask = mask>>1 ) + { + bit = ss_features.data.contents[i]&mask; + if (bit) + { + str=ops_show_ss_feature ( bit, i ); + if (!add_str_from_bit_map( text, str, bit)) + { + ops_text_free(text); + return NULL; + } + } + } + } + return text; + } + +/** + * \ingroup Core_Print + * returns description of SS Key Flag + * \param octet + * \param map + * \return +*/ +const char *ops_show_ss_key_flag(unsigned char octet, ops_bit_map_t *map) + { + return str_from_bitfield(octet,map); + } + +/** + * \ingroup Core_Print + * + * returns set of descriptions of the given Preferred Key Flags + * \param ss_key_flags Array of Key Flags + * \return NULL if cannot allocate memory or other error + * \return pointer to structure, if no error + */ +ops_text_t *ops_showall_ss_key_flags(ops_ss_key_flags_t ss_key_flags) + { + ops_text_t *text=NULL; + const char *str; + int i=0; + unsigned char mask, bit; + + text=malloc(sizeof(ops_text_t)); + if (!text) + return NULL; + + ops_text_init(text); + + /* xxx - TBD: extend to handle multiple octets of bits - rachel */ + + for (i=0,mask=0x80 ; i < 8 ; i++,mask=mask >> 1) + { + bit=ss_key_flags.data.contents[0]&mask; + if(bit) + { + str=ops_show_ss_key_flag(bit,&ss_key_flags_map[0]); + if(!add_str_from_bit_map(text,strdup(str),bit)) + { + ops_text_free(text); + return NULL; + } + } + } +/* xxx - must add error text if more than one octet. Only one currently specified -- rachel */ + return text; + } + +/** + * \ingroup Core_Print + * + * returns description of one given Key Server Preference + * + * \param prefs Byte containing bitfield of preferences + * \param map + * \return string or "Unknown" + */ +const char *ops_show_ss_key_server_prefs(unsigned char prefs, + ops_bit_map_t *map) + { + return str_from_bitfield(prefs,map); + } + +/** + * \ingroup Core_Print + * returns set of descriptions of given Key Server Preferences + * \param ss_key_server_prefs + * \return NULL if cannot allocate memory or other error + * \return pointer to structure, if no error + * +*/ +ops_text_t *ops_showall_ss_key_server_prefs(ops_ss_key_server_prefs_t ss_key_server_prefs) + { + ops_text_t *text=NULL; + const char *str; + int i=0; + unsigned char mask, bit; + + text=malloc(sizeof(ops_text_t)); + if (!text) + return NULL; + + ops_text_init(text); + + /* xxx - TBD: extend to handle multiple octets of bits - rachel */ + + for (i=0,mask=0x80 ; i < 8 ; i++,mask=mask >> 1) + { + bit=ss_key_server_prefs.data.contents[0]&mask; + if (bit) + { + str=ops_show_ss_key_server_prefs(bit, + &ss_key_server_prefs_map[0]); + if(!add_str_from_bit_map( text, strdup(str), bit)) + { + ops_text_free(text); + return NULL; + } + } + } +/* xxx - must add error text if more than one octet. Only one currently specified -- rachel */ + return text; + } + +/** + * \ingroup Core_Print + * + * returns set of descriptions of the given SS Notation Data Flags + * \param ss_notation_data Signature Sub-Packet Notation Data + * \return NULL if cannot allocate memory or other error + * \return pointer to structure, if no error + */ +ops_text_t *ops_showall_ss_notation_data_flags(ops_ss_notation_data_t ss_notation_data) + { + return showall_octets_bits(&ss_notation_data.flags,ss_notation_data_map, + OPS_ARRAY_SIZE(ss_notation_data_map)); + } diff --git a/openpgpsdk/src/parse_local.h b/openpgpsdk/src/parse_local.h new file mode 100644 index 000000000..524f251dc --- /dev/null +++ b/openpgpsdk/src/parse_local.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file */ + +#include +#include + +/** ops_reader_info */ +struct ops_reader_info + { + ops_reader_t *reader; /*!< the reader function to use to get the + data to be parsed */ + ops_reader_destroyer_t *destroyer; + void *arg; /*!< the args to pass to the reader function */ + + ops_boolean_t accumulate:1; /*!< set to accumulate packet data */ + unsigned char *accumulated; /*!< the accumulated data */ + unsigned asize; /*!< size of the buffer */ + unsigned alength; /*!< used buffer */ + /* XXX: what do we do about offsets into compressed packets? */ + unsigned position; /*!< the offset from the beginning (with this reader) */ + + ops_reader_info_t *next; + ops_parse_info_t *pinfo; /*!< A pointer back to the parent parse_info structure */ + }; + + +/** ops_crypt_info + Encrypt/decrypt settings +*/ +struct ops_crypt_info + { + char *passphrase; /* + +#include + +void ops_random(void *dest,size_t length) + { + RAND_bytes(dest,length); + } diff --git a/openpgpsdk/src/reader.c b/openpgpsdk/src/reader.c new file mode 100644 index 000000000..341542b2f --- /dev/null +++ b/openpgpsdk/src/reader.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#ifndef WIN32 +#include +#else +#include +#endif +#include +#include +#include + +#include +#include + +#include "parse_local.h" + + +/** + * \ingroup Internal_Readers_Generic + * \brief Starts reader stack + * \param pinfo Parse settings + * \param reader Reader to use + * \param destroyer Destroyer to use + * \param arg Reader-specific arg + */ +void ops_reader_set(ops_parse_info_t *pinfo,ops_reader_t *reader,ops_reader_destroyer_t *destroyer,void *arg) + { + pinfo->rinfo.reader=reader; + pinfo->rinfo.destroyer=destroyer; + pinfo->rinfo.arg=arg; + } + +/** + * \ingroup Internal_Readers_Generic + * \brief Adds to reader stack + * \param pinfo Parse settings + * \param reader Reader to use + * \param destroyer Reader's destroyer + * \param arg Reader-specific arg + */ +void ops_reader_push(ops_parse_info_t *pinfo,ops_reader_t *reader,ops_reader_destroyer_t *destroyer,void *arg) + { + ops_reader_info_t *rinfo=malloc(sizeof *rinfo); + + *rinfo=pinfo->rinfo; + memset(&pinfo->rinfo,'\0',sizeof pinfo->rinfo); + pinfo->rinfo.next=rinfo; + pinfo->rinfo.pinfo=pinfo; + + // should copy accumulate flags from other reader? RW + pinfo->rinfo.accumulate=rinfo->accumulate; + + ops_reader_set(pinfo,reader,destroyer,arg); + } + +/** + * \ingroup Internal_Readers_Generic + * \brief Removes from reader stack + * \param pinfo Parse settings + */ +void ops_reader_pop(ops_parse_info_t *pinfo) + { + ops_reader_info_t *next=pinfo->rinfo.next; + + pinfo->rinfo=*next; + free(next); + } + +/** + * \ingroup Internal_Readers_Generic + * \brief Gets arg from reader + * \param rinfo Reader info + * \return Pointer to reader info's arg + */ +void *ops_reader_get_arg(ops_reader_info_t *rinfo) + { return rinfo->arg; } + +/** + * \ingroup Internal_Readers_Generic + * \brief Gets reader's arg from parse_info + * \param pinfo + * \return Pointer to parse_info's reader_info's arg + */ +void *ops_reader_get_arg_from_pinfo(ops_parse_info_t *pinfo) + { return pinfo->rinfo.arg; } + +// EOF diff --git a/openpgpsdk/src/reader_armoured.c b/openpgpsdk/src/reader_armoured.c new file mode 100644 index 000000000..dc8ca8f02 --- /dev/null +++ b/openpgpsdk/src/reader_armoured.c @@ -0,0 +1,1046 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + * \brief Code for dealing with ASCII-armoured packets + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "parse_local.h" + +#include +#include + +#include + +static int debug=0; + +#define CRC24_POLY 0x1864cfbL + +/** + * \struct dearmour_arg_t + */ +typedef struct + { + enum + { + OUTSIDE_BLOCK=0, + BASE64, + AT_TRAILER_NAME, + } state; + + enum + { + NONE=0, + BEGIN_PGP_MESSAGE, + BEGIN_PGP_PUBLIC_KEY_BLOCK, + BEGIN_PGP_PRIVATE_KEY_BLOCK, + BEGIN_PGP_MULTI, + BEGIN_PGP_SIGNATURE, + + END_PGP_MESSAGE, + END_PGP_PUBLIC_KEY_BLOCK, + END_PGP_PRIVATE_KEY_BLOCK, + END_PGP_MULTI, + END_PGP_SIGNATURE, + + BEGIN_PGP_SIGNED_MESSAGE + } lastseen; + + ops_parse_info_t *parse_info; + ops_boolean_t seen_nl:1; + ops_boolean_t prev_nl:1; + ops_boolean_t allow_headers_without_gap:1; /*!< allow headers in + armoured data that + are not separated + from the data by a + blank line */ + ops_boolean_t allow_no_gap:1; /*!< allow no blank line at the + start of armoured data */ + ops_boolean_t allow_trailing_whitespace:1; /*!< allow armoured + stuff to have + trailing whitespace + where we wouldn't + strictly expect it */ + + // it is an error to get a cleartext message without a sig + ops_boolean_t expect_sig:1; + ops_boolean_t got_sig:1; + + // base64 stuff + unsigned buffered; + unsigned char buffer[3]; + ops_boolean_t eof64; + unsigned long checksum; + unsigned long read_checksum; + // unarmoured text blocks + unsigned char unarmoured[8192]; + size_t num_unarmoured; + // pushed back data (stored backwards) + unsigned char *pushed_back; + unsigned npushed_back; + // armoured block headers + ops_headers_t headers; + } dearmour_arg_t; + +static void push_back(dearmour_arg_t *arg,const unsigned char *buf, + unsigned length) + { + unsigned n; + + assert(!arg->pushed_back); + arg->pushed_back=malloc(length); + for(n=0 ; n < length ; ++n) + arg->pushed_back[n]=buf[length-n-1]; + arg->npushed_back=length; + } + +static int set_lastseen_headerline(dearmour_arg_t* arg, char* buf, ops_error_t **errors) + { + char* begin_msg="BEGIN PGP MESSAGE"; + char* begin_public="BEGIN PGP PUBLIC KEY BLOCK"; + char* begin_private="BEGIN PGP PRIVATE KEY BLOCK"; + char* begin_multi="BEGIN PGP MESSAGE, PART "; + char* begin_sig="BEGIN PGP SIGNATURE"; + + char* end_msg="END PGP MESSAGE"; + char* end_public="END PGP PUBLIC KEY BLOCK"; + char* end_private="END PGP PRIVATE KEY BLOCK"; + char* end_multi="END PGP MESSAGE, PART "; + char* end_sig="END PGP SIGNATURE"; + + char* begin_signed_msg="BEGIN PGP SIGNED MESSAGE"; + + int prev=arg->lastseen; + + if (!strncmp(buf,begin_msg,strlen(begin_msg))) + arg->lastseen=BEGIN_PGP_MESSAGE; + else if (!strncmp(buf,begin_public,strlen(begin_public))) + arg->lastseen=BEGIN_PGP_PUBLIC_KEY_BLOCK; + else if (!strncmp(buf,begin_private,strlen(begin_private))) + arg->lastseen=BEGIN_PGP_PRIVATE_KEY_BLOCK; + else if (!strncmp(buf,begin_multi,strlen(begin_multi))) + arg->lastseen=BEGIN_PGP_MULTI; + else if (!strncmp(buf,begin_sig,strlen(begin_sig))) + arg->lastseen=BEGIN_PGP_SIGNATURE; + + else if (!strncmp(buf,end_msg,strlen(end_msg))) + arg->lastseen=END_PGP_MESSAGE; + else if (!strncmp(buf,end_public,strlen(end_public))) + arg->lastseen=END_PGP_PUBLIC_KEY_BLOCK; + else if (!strncmp(buf,end_private,strlen(end_private))) + arg->lastseen=END_PGP_PRIVATE_KEY_BLOCK; + else if (!strncmp(buf,end_multi,strlen(end_multi))) + arg->lastseen=END_PGP_MULTI; + else if (!strncmp(buf,end_sig,strlen(end_sig))) + arg->lastseen=END_PGP_SIGNATURE; + + else if (!strncmp(buf,begin_signed_msg,strlen(begin_signed_msg))) + arg->lastseen=BEGIN_PGP_SIGNED_MESSAGE; + + else + { + OPS_ERROR_1(errors,OPS_E_R_BAD_FORMAT,"Unrecognised Header Line %s", buf); + return 0; + } + + if (debug) + printf("set header: buf=%s, arg->lastseen=%d, prev=%d\n", buf, arg->lastseen, prev); + + switch (arg->lastseen) + { + case NONE: + OPS_ERROR_1(errors,OPS_E_R_BAD_FORMAT,"Unrecognised last seen Header Line %s", buf); + break; + + case END_PGP_MESSAGE: + if (prev!=BEGIN_PGP_MESSAGE) + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"Got END PGP MESSAGE, but not after BEGIN"); + break; + + case END_PGP_PUBLIC_KEY_BLOCK: + if (prev!=BEGIN_PGP_PUBLIC_KEY_BLOCK) + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"Got END PGP PUBLIC KEY BLOCK, but not after BEGIN"); + break; + + case END_PGP_PRIVATE_KEY_BLOCK: + if (prev!=BEGIN_PGP_PRIVATE_KEY_BLOCK) + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"Got END PGP PRIVATE KEY BLOCK, but not after BEGIN"); + break; + + case BEGIN_PGP_MULTI: + case END_PGP_MULTI: + OPS_ERROR(errors,OPS_E_R_UNSUPPORTED,"Multi-part messages are not yet supported"); + break; + + case END_PGP_SIGNATURE: + if (prev!=BEGIN_PGP_SIGNATURE) + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"Got END PGP SIGNATURE, but not after BEGIN"); + break; + + case BEGIN_PGP_MESSAGE: + case BEGIN_PGP_PUBLIC_KEY_BLOCK: + case BEGIN_PGP_PRIVATE_KEY_BLOCK: + case BEGIN_PGP_SIGNATURE: + case BEGIN_PGP_SIGNED_MESSAGE: + break; + } + + return 1; + } + +static int read_char(dearmour_arg_t *arg,ops_error_t **errors, + ops_reader_info_t *rinfo, + ops_parse_cb_info_t *cbinfo, + ops_boolean_t skip) + { + unsigned char c[1]; + + do + { + if(arg->npushed_back) + { + c[0]=arg->pushed_back[--arg->npushed_back]; + if(!arg->npushed_back) + { + free(arg->pushed_back); + arg->pushed_back=NULL; + } + } + /* XXX: should ops_stacked_read exist? Shouldn't this be a limited_read? */ + else if(ops_stacked_read(c,1,errors,rinfo,cbinfo) != 1) + return -1; + } + while(skip && c[0] == '\r'); + + arg->prev_nl=arg->seen_nl; + arg->seen_nl=c[0] == '\n'; + + return c[0]; + } + +static int eat_whitespace(int first, + dearmour_arg_t *arg,ops_error_t **errors, + ops_reader_info_t *rinfo, + ops_parse_cb_info_t *cbinfo, + ops_boolean_t skip) + { + int c=first; + + while(c == ' ' || c == '\t') + c=read_char(arg,errors,rinfo,cbinfo,skip); + + return c; + } + +static int read_and_eat_whitespace(dearmour_arg_t *arg, + ops_error_t **errors, + ops_reader_info_t *rinfo, + ops_parse_cb_info_t *cbinfo, + ops_boolean_t skip) + { + int c; + + do + c=read_char(arg,errors,rinfo,cbinfo,skip); + while(c == ' ' || c == '\t'); + + return c; + } + +static void flush(dearmour_arg_t *arg,ops_parse_cb_info_t *cbinfo) + { + ops_parser_content_t content; + + if(arg->num_unarmoured == 0) + return; + + content.content.unarmoured_text.data=arg->unarmoured; + content.content.unarmoured_text.length=arg->num_unarmoured; + CB(cbinfo,OPS_PTAG_CT_UNARMOURED_TEXT,&content); + arg->num_unarmoured=0; + } + +static int unarmoured_read_char(dearmour_arg_t *arg,ops_error_t **errors, + ops_reader_info_t *rinfo, + ops_parse_cb_info_t *cbinfo, + ops_boolean_t skip) + { + int c; + + do + { + c=read_char(arg,errors,rinfo,cbinfo,ops_false); + if(c < 0) + return c; + arg->unarmoured[arg->num_unarmoured++]=c; + if(arg->num_unarmoured == sizeof arg->unarmoured) + flush(arg,cbinfo); + } + while(skip && c == '\r'); + + return c; + } + +/** + * \param headers + * \param key + * + * \return header value if found, otherwise NULL + */ +const char *ops_find_header(ops_headers_t *headers,const char *key) + { + unsigned n; + + for(n=0 ; n < headers->nheaders ; ++n) + if(!strcmp(headers->headers[n].key,key)) + return headers->headers[n].value; + return NULL; + } + +/** + * \param dest + * \param src + */ +void ops_dup_headers(ops_headers_t *dest,const ops_headers_t *src) + { + unsigned n; + + dest->headers=malloc(src->nheaders*sizeof *dest->headers); + dest->nheaders=src->nheaders; + + for(n=0 ; n < src->nheaders ; ++n) + { + dest->headers[n].key=strdup(src->headers[n].key); + dest->headers[n].value=strdup(src->headers[n].value); + } + } + +/* Note that this skips CRs so implementations always see just + straight LFs as line terminators */ +static int process_dash_escaped(dearmour_arg_t *arg,ops_error_t **errors, + ops_reader_info_t *rinfo, + ops_parse_cb_info_t *cbinfo) + { + ops_parser_content_t content; + ops_parser_content_t content2; + ops_signed_cleartext_body_t *body=&content.content.signed_cleartext_body; + ops_signed_cleartext_trailer_t *trailer + =&content2.content.signed_cleartext_trailer; + const char *hashstr; + ops_hash_t *hash; + int total; + + hash=malloc(sizeof *hash); + hashstr=ops_find_header(&arg->headers,"Hash"); + if(hashstr) + { + ops_hash_algorithm_t alg; + + alg=ops_hash_algorithm_from_text(hashstr); + + if(!ops_is_hash_alg_supported(&alg)) + { + free(hash); + OPS_ERROR_1(errors,OPS_E_R_BAD_FORMAT,"Unsupported hash algorithm '%s'",hashstr); + return -1; + } + if(alg == OPS_HASH_UNKNOWN) + { + free(hash); + OPS_ERROR_1(errors,OPS_E_R_BAD_FORMAT,"Unknown hash algorithm '%s'",hashstr); + return -1; + } + ops_hash_any(hash,alg); + } + else + ops_hash_md5(hash); + + hash->init(hash); + + body->length=0; + total=0; + for( ; ; ) + { + int c; + unsigned count; + + if((c=read_char(arg,errors,rinfo,cbinfo,ops_true)) < 0) + return -1; + if(arg->prev_nl && c == '-') + { + if((c=read_char(arg,errors,rinfo,cbinfo,ops_false)) < 0) + return -1; + if(c != ' ') + { + /* then this had better be a trailer! */ + if(c != '-') + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"Bad dash-escaping"); + for(count=2 ; count < 5 ; ++count) + { + if((c=read_char(arg,errors,rinfo,cbinfo,ops_false)) < 0) + return -1; + if(c != '-') + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"Bad dash-escaping (2)"); + } + arg->state=AT_TRAILER_NAME; + break; + } + /* otherwise we read the next character */ + if((c=read_char(arg,errors,rinfo,cbinfo,ops_false)) < 0) + return -1; + } + if(c == '\n' && body->length) + { + assert(memchr(body->data+1,'\n',body->length-1) == NULL); + if(body->data[0] == '\n') + hash->add(hash,(unsigned char *)"\r",1); + hash->add(hash,body->data,body->length); + if (debug) + { fprintf(stderr,"Got body:\n%s\n",body->data); } + CB(cbinfo,OPS_PTAG_CT_SIGNED_CLEARTEXT_BODY,&content); + body->length=0; + } + + body->data[body->length++]=c; + ++total; + if(body->length == sizeof body->data) + { + if (debug) + { fprintf(stderr,"Got body (2):\n%s\n",body->data); } + CB(cbinfo,OPS_PTAG_CT_SIGNED_CLEARTEXT_BODY,&content); + body->length=0; + } + } + + assert(body->data[0] == '\n'); + assert(body->length == 1); + /* don't send that one character, because its part of the trailer. */ + + trailer->hash=hash; + CB(cbinfo,OPS_PTAG_CT_SIGNED_CLEARTEXT_TRAILER,&content2); + + return total; + } + +static int add_header(dearmour_arg_t *arg,const char *key,const char + *value) + { + /* + * Check that the header is valid + */ + if ( !strcmp(key,"Version") || !strcmp(key,"Comment") + || !strcmp(key,"MessageID") || !strcmp(key,"Hash") + || !strcmp(key,"Charset")) + { + arg->headers.headers=realloc(arg->headers.headers, + (arg->headers.nheaders+1) + *sizeof *arg->headers.headers); + arg->headers.headers[arg->headers.nheaders].key=strdup(key); + arg->headers.headers[arg->headers.nheaders].value=strdup(value); + ++arg->headers.nheaders; + return 1; + } + else + { + return 0; + } + } + +/* \todo what does a return value of 0 indicate? 1 is good, -1 is bad */ +static int parse_headers(dearmour_arg_t *arg,ops_error_t **errors, + ops_reader_info_t *rinfo,ops_parse_cb_info_t *cbinfo) + { + int rtn=1; + char *buf; + unsigned nbuf; + unsigned size; + ops_boolean_t first=ops_true; + //ops_parser_content_t content; + + buf=NULL; + nbuf=size=0; + + for( ; ; ) + { + int c; + + if((c=read_char(arg,errors,rinfo,cbinfo,ops_true)) < 0) + { + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"Unexpected EOF"); + rtn=-1; + break; + } + + if(c == '\n') + { + char *s; + + if(nbuf == 0) + break; + + assert(nbuf < size); + buf[nbuf]='\0'; + + s=strchr(buf,':'); + if(!s) + if(!first && !arg->allow_headers_without_gap) + { + // then we have seriously malformed armour + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"No colon in armour header"); + rtn=-1; + break; + } + else + { + if(first && + !(arg->allow_headers_without_gap || arg->allow_no_gap)) + { + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"No colon in armour header (2)"); + // then we have a nasty armoured block with no + // headers, not even a blank line. + buf[nbuf]='\n'; + push_back(arg,(unsigned char *)buf,nbuf+1); + rtn=-1; + break; + } + } + else + { + *s='\0'; + if(s[1] != ' ') + { + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"No space in armour header"); + rtn=-1; + goto end; + } + if (!add_header(arg,buf,s+2)) + { + OPS_ERROR_1(errors,OPS_E_R_BAD_FORMAT,"Invalid header %s", buf); + rtn=-1; + goto end; + } + nbuf=0; + } + first=ops_false; + } + else + { + if(size <= nbuf+1) + { + size+=size+80; + buf=realloc(buf,size); + } + buf[nbuf++]=c; + } + } + + end: + free(buf); + + return rtn; + } + +static int read4(dearmour_arg_t *arg,ops_error_t **errors, + ops_reader_info_t *rinfo,ops_parse_cb_info_t *cbinfo, + int *pc,unsigned *pn,unsigned long *pl) + { + int n,c; + unsigned long l=0; + + for(n=0 ; n < 4 ; ++n) + { + c=read_char(arg,errors,rinfo,cbinfo,ops_true); + if(c < 0) + { + arg->eof64=ops_true; + return -1; + } + if(c == '-') + break; + if(c == '=') + break; + l <<= 6; + if(c >= 'A' && c <= 'Z') + l+=c-'A'; + else if(c >= 'a' && c <= 'z') + l+=c-'a'+26; + else if(c >= '0' && c <= '9') + l+=c-'0'+52; + else if(c == '+') + l+=62; + else if(c == '/') + l+=63; + else + { + --n; + l >>= 6; + } + } + + *pc=c; + *pn=n; + *pl=l; + + return 4; + } + +unsigned ops_crc24(unsigned checksum,unsigned char c) + { + unsigned i; + + checksum ^= c << 16; + for(i=0 ; i < 8 ; i++) + { + checksum <<= 1; + if(checksum & 0x1000000) + checksum ^= CRC24_POLY; + } + return checksum&0xffffffL; + } + +static int decode64(dearmour_arg_t *arg,ops_error_t **errors, + ops_reader_info_t *rinfo,ops_parse_cb_info_t *cbinfo) + { + unsigned n; + int n2; + unsigned long l; + int c; + int ret; + + assert(arg->buffered == 0); + + ret=read4(arg,errors,rinfo,cbinfo,&c,&n,&l); + if(ret < 0) + { + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"Badly formed base64"); + return 0; + } + + if(n == 3) + { + if(c != '=') + { + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"Badly terminated base64 (2)"); + return 0; + } + arg->buffered=2; + arg->eof64=ops_true; + l >>= 2; + } + else if(n == 2) + { + if(c != '=') + { + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"Badly terminated base64 (3)"); + return 0; + } + arg->buffered=1; + arg->eof64=ops_true; + l >>= 4; + c=read_char(arg,errors,rinfo,cbinfo,ops_false); + if(c != '=') + { + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"Badly terminated base64"); + return 0; + } + } + else if(n == 0) + { + if(!arg->prev_nl || c != '=') + { + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"Badly terminated base64 (4)"); + return 0; + } + arg->buffered=0; + } + else + { + assert(n == 4); + arg->buffered=3; + assert(c != '-' && c != '='); + } + + if(arg->buffered < 3 && arg->buffered > 0) + { + // then we saw padding + assert(c == '='); + c=read_and_eat_whitespace(arg,errors,rinfo,cbinfo,ops_true); + if(c != '\n') + { + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"No newline at base64 end"); + return 0; + } + c=read_char(arg,errors,rinfo,cbinfo,ops_false); + if(c != '=') + { + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"No checksum at base64 end"); + return 0; + } + } + + if(c == '=') + { + // now we are at the checksum + ret=read4(arg,errors,rinfo,cbinfo,&c,&n,&arg->read_checksum); + if(ret < 0 || n != 4) + { + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"Error in checksum"); + return 0; + } + c=read_char(arg,errors,rinfo,cbinfo,ops_true); + if(arg->allow_trailing_whitespace) + c=eat_whitespace(c,arg,errors,rinfo,cbinfo,ops_true); + if(c != '\n') + { + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"Badly terminated checksum"); + return 0; + } + c=read_char(arg,errors,rinfo,cbinfo,ops_false); + if(c != '-') + { + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"Bad base64 trailer (2)"); + return 0; + } + } + + if(c == '-') + { + for(n=0 ; n < 4 ; ++n) + if(read_char(arg,errors,rinfo,cbinfo,ops_false) != '-') + { + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"Bad base64 trailer"); + return 0; + } + arg->eof64=ops_true; + } + else + assert(arg->buffered); + + for(n=0 ; n < arg->buffered ; ++n) + { + arg->buffer[n]=l; + l >>= 8; + } + + for(n2=arg->buffered-1 ; n2 >= 0 ; --n2) + arg->checksum=ops_crc24(arg->checksum,arg->buffer[n2]); + + if(arg->eof64 && arg->read_checksum != arg->checksum) + { + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"Checksum mismatch"); + return 0; + } + + return 1; + } + +static void base64(dearmour_arg_t *arg) + { + arg->state=BASE64; + arg->checksum=CRC24_INIT; + arg->eof64=ops_false; + arg->buffered=0; + } + +// This reader is rather strange in that it can generate callbacks for +// content - this is because plaintext is not encapsulated in PGP +// packets... it also calls back for the text between the blocks. + +static int armoured_data_reader(void *dest_,size_t length,ops_error_t **errors, + ops_reader_info_t *rinfo, + ops_parse_cb_info_t *cbinfo) + { + dearmour_arg_t *arg=ops_reader_get_arg(rinfo); + ops_parser_content_t content; + int ret; + ops_boolean_t first; + unsigned char *dest=dest_; + int saved=length; + + if(arg->eof64 && !arg->buffered) + assert(arg->state == OUTSIDE_BLOCK || arg->state == AT_TRAILER_NAME); + + while(length > 0) + { + unsigned count; + unsigned n; + char buf[1024]; + int c; + + flush(arg,cbinfo); + switch(arg->state) + { + case OUTSIDE_BLOCK: + /* This code returns EOF rather than EARLY_EOF because if + we don't see a header line at all, then it is just an + EOF (and not a BLOCK_END) */ + while(!arg->seen_nl) + if((c=unarmoured_read_char(arg,errors,rinfo,cbinfo,ops_true)) < 0) + return 0; + + /* flush at this point so we definitely have room for the + header, and so we can easily erase it from the buffer */ + flush(arg,cbinfo); + /* Find and consume the 5 leading '-' */ + for(count=0 ; count < 5 ; ++count) + { + if((c=unarmoured_read_char(arg,errors,rinfo,cbinfo,ops_false)) < 0) + return 0; + if(c != '-') + goto reloop; + } + + /* Now find the block type */ + for(n=0 ; n < sizeof buf-1 ; ) + { + if((c=unarmoured_read_char(arg,errors,rinfo,cbinfo,ops_false)) < 0) + return 0; + if(c == '-') + goto got_minus; + buf[n++]=c; + } + /* then I guess this wasn't a proper header */ + break; + + got_minus: + buf[n]='\0'; + + /* Consume trailing '-' */ + for(count=1 ; count < 5 ; ++count) + { + if((c=unarmoured_read_char(arg,errors,rinfo,cbinfo,ops_false)) < 0) + return 0; + if(c != '-') + /* wasn't a header after all */ + goto reloop; + } + + /* Consume final NL */ + if((c=unarmoured_read_char(arg,errors,rinfo,cbinfo,ops_true)) < 0) + return 0; + if(arg->allow_trailing_whitespace) + if((c=eat_whitespace(c,arg,errors,rinfo,cbinfo, + ops_true)) < 0) + return 0; + if(c != '\n') + /* wasn't a header line after all */ + break; + + /* Now we've seen the header, scrub it from the buffer */ + arg->num_unarmoured=0; + + /* But now we've seen a header line, then errors are + EARLY_EOF */ + if((ret=parse_headers(arg,errors,rinfo,cbinfo)) <= 0) + return -1; + + if (!set_lastseen_headerline(arg,buf,errors)) + return -1; + + if(!strcmp(buf,"BEGIN PGP SIGNED MESSAGE")) + { + ops_dup_headers(&content.content.signed_cleartext_header.headers,&arg->headers); + CB(cbinfo,OPS_PTAG_CT_SIGNED_CLEARTEXT_HEADER,&content); + ret=process_dash_escaped(arg,errors,rinfo,cbinfo); + if(ret <= 0) + return ret; + } + else + { + content.content.armour_header.type=buf; + content.content.armour_header.headers=arg->headers; + memset(&arg->headers,'\0',sizeof arg->headers); + CB(cbinfo,OPS_PTAG_CT_ARMOUR_HEADER,&content); + base64(arg); + } + break; + + case BASE64: + first=ops_true; + while(length > 0) + { + if(!arg->buffered) + { + if(!arg->eof64) + { + ret=decode64(arg,errors,rinfo,cbinfo); + if(ret <= 0) + return ret; + } + if(!arg->buffered) + { + assert(arg->eof64); + if(first) + { + arg->state=AT_TRAILER_NAME; + goto reloop; + } + return -1; + } + } + + assert(arg->buffered); + *dest=arg->buffer[--arg->buffered]; + ++dest; + --length; + first=ops_false; + } + if(arg->eof64 && !arg->buffered) + arg->state=AT_TRAILER_NAME; + break; + + case AT_TRAILER_NAME: + for(n=0 ; n < sizeof buf-1 ; ) + { + if((c=read_char(arg,errors,rinfo,cbinfo,ops_false)) < 0) + return -1; + if(c == '-') + goto got_minus2; + buf[n++]=c; + } + /* then I guess this wasn't a proper trailer */ + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"Bad ASCII armour trailer"); + break; + + got_minus2: + buf[n]='\0'; + + if (!set_lastseen_headerline(arg,buf,errors)) + return -1; + + /* Consume trailing '-' */ + for(count=1 ; count < 5 ; ++count) + { + if((c=read_char(arg,errors,rinfo,cbinfo,ops_false)) < 0) + return -1; + if(c != '-') + /* wasn't a trailer after all */ + OPS_ERROR(errors, OPS_E_R_BAD_FORMAT,"Bad ASCII armour trailer (2)"); + } + + /* Consume final NL */ + if((c=read_char(arg,errors,rinfo,cbinfo,ops_true)) < 0) + return -1; + if(arg->allow_trailing_whitespace) + if((c=eat_whitespace(c,arg,errors,rinfo,cbinfo, + ops_true)) < 0) + return 0; + if(c != '\n') + /* wasn't a trailer line after all */ + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"Bad ASCII armour trailer (3)"); + + if(!strncmp(buf,"BEGIN ",6)) + { + if (!set_lastseen_headerline(arg,buf,errors)) + return -1; + if((ret=parse_headers(arg,errors,rinfo,cbinfo)) <= 0) + return ret; + content.content.armour_header.type=buf; + content.content.armour_header.headers=arg->headers; + memset(&arg->headers,'\0',sizeof arg->headers); + CB(cbinfo,OPS_PTAG_CT_ARMOUR_HEADER,&content); + base64(arg); + } + else + { + content.content.armour_trailer.type=buf; + CB(cbinfo,OPS_PTAG_CT_ARMOUR_TRAILER,&content); + arg->state=OUTSIDE_BLOCK; + } + break; + } + reloop: + continue; + } + + return saved; + } + +static void armoured_data_destroyer(ops_reader_info_t *rinfo) + { free(ops_reader_get_arg(rinfo)); } + +/** + * \ingroup Core_Readers_Armour + * \brief Pushes dearmouring reader onto stack + * \param parse_info Usual structure containing information about to how to do the parse + * \sa ops_reader_pop_dearmour() + */ +void ops_reader_push_dearmour(ops_parse_info_t *parse_info) + /* + This function originally had these parameters to cater for + packets which didn't strictly match the RFC. + The initial 0.5 release is only going to support + strict checking. + If it becomes desirable to support loose checking of armoured packets + and these params are reinstated, parse_headers() must be fixed + so that these flags work correctly. + + // Allow headers in armoured data that are not separated from the data by a blank line + ops_boolean_t without_gap, + + // Allow no blank line at the start of armoured data + ops_boolean_t no_gap, + + //Allow armoured data to have trailing whitespace where we strictly would not expect it + ops_boolean_t trailing_whitespace + */ + { + dearmour_arg_t *arg; + + arg=ops_mallocz(sizeof *arg); + arg->seen_nl=ops_true; +/* + arg->allow_headers_without_gap=without_gap; + arg->allow_no_gap=no_gap; + arg->allow_trailing_whitespace=trailing_whitespace; +*/ + arg->expect_sig=ops_false; + arg->got_sig=ops_false; + + ops_reader_push(parse_info,armoured_data_reader,armoured_data_destroyer,arg); + } + +/** + * \ingroup Core_Readers_Armour + * \brief Pops dearmour reader from stock + * \param pinfo + * \sa ops_reader_push_dearmour() + */ +void ops_reader_pop_dearmour(ops_parse_info_t *pinfo) + { + dearmour_arg_t *arg=ops_reader_get_arg(ops_parse_get_rinfo(pinfo)); + free(arg); + ops_reader_pop(pinfo); + } + +// EOF diff --git a/openpgpsdk/src/reader_encrypted_se.c b/openpgpsdk/src/reader_encrypted_se.c new file mode 100644 index 000000000..54ef2086d --- /dev/null +++ b/openpgpsdk/src/reader_encrypted_se.c @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#ifndef OPENSSL_NO_IDEA +#include +#endif +#include +#include +#include "parse_local.h" + +#include +#include + +static int debug=0; + +#ifndef ATTRIBUTE_UNUSED + +#ifndef WIN32 +#define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +#else +#define ATTRIBUTE_UNUSED +#endif // #ifndef WIN32 + +#endif /* ATTRIBUTE_UNUSED */ + + +// \todo there's also a encrypted_arg_t in adv_create.c +// which is used for *encrypting* whereas this is used +// for *decrypting* + +typedef struct + { + unsigned char decrypted[1024]; + size_t decrypted_count; + size_t decrypted_offset; + ops_crypt_t *decrypt; + ops_region_t *region; + ops_boolean_t prev_read_was_plain:1; + } encrypted_arg_t; + +static int encrypted_data_reader(void *dest,size_t length,ops_error_t **errors, + ops_reader_info_t *rinfo, + ops_parse_cb_info_t *cbinfo) + { + encrypted_arg_t *arg=ops_reader_get_arg(rinfo); + int saved=length; + + // V3 MPIs have the count plain and the cipher is reset after each count + if(arg->prev_read_was_plain && !rinfo->pinfo->reading_mpi_length) + { + assert(rinfo->pinfo->reading_v3_secret); + arg->decrypt->decrypt_resync(arg->decrypt); + arg->prev_read_was_plain=ops_false; + } + else if(rinfo->pinfo->reading_v3_secret + && rinfo->pinfo->reading_mpi_length) + { + arg->prev_read_was_plain=ops_true; + } + + while(length > 0) + { + if(arg->decrypted_count) + { + + unsigned n; + + // if we are reading v3 we should never read more than + // we're asked for + assert(length >= arg->decrypted_count + || (!rinfo->pinfo->reading_v3_secret + && !rinfo->pinfo->exact_read)); + + if(length > arg->decrypted_count) + n=arg->decrypted_count; + else + n=length; + + memcpy(dest,arg->decrypted+arg->decrypted_offset,n); + arg->decrypted_count-=n; + arg->decrypted_offset+=n; + length-=n; +#ifdef WIN32 + (char*)dest+=n; +#else + dest+=n; +#endif + } + else + { + unsigned n=arg->region->length; + unsigned char buffer[1024]; + + if(!n) + { + return -1; + } + + if(!arg->region->indeterminate) + { + n-=arg->region->length_read; + if(n == 0) + return saved-length; + if(n > sizeof buffer) + n=sizeof buffer; + } + else + { + n=sizeof buffer; + } + + // we can only read as much as we're asked for in v3 keys + // because they're partially unencrypted! + if((rinfo->pinfo->reading_v3_secret || rinfo->pinfo->exact_read) + && n > length) + n=length; + + if(!ops_stacked_limited_read(buffer,n,arg->region,errors,rinfo, + cbinfo)) + { + return -1; + } + + if(!rinfo->pinfo->reading_v3_secret + || !rinfo->pinfo->reading_mpi_length) + { + arg->decrypted_count=ops_decrypt_se_ip(arg->decrypt, + arg->decrypted, + buffer,n); + + if (debug) + { + fprintf(stderr,"READING:\nencrypted: "); + int i=0; + for (i=0; i<16; i++) + fprintf(stderr,"%2x ", buffer[i]); + fprintf(stderr,"\n"); + fprintf(stderr,"decrypted: "); + for (i=0; i<16; i++) + fprintf(stderr,"%2x ", arg->decrypted[i]); + fprintf(stderr,"\n"); + } + } + else + { + memcpy(arg->decrypted,buffer,n); + arg->decrypted_count=n; + } + + assert(arg->decrypted_count > 0); + + arg->decrypted_offset=0; + } + } + + return saved; + } + +static void encrypted_data_destroyer(ops_reader_info_t *rinfo) + { free(ops_reader_get_arg(rinfo)); } + +/** + * \ingroup Core_Readers_SE + * \brief Pushes decryption reader onto stack + * \sa ops_reader_pop_decrypt() + */ +void ops_reader_push_decrypt(ops_parse_info_t *pinfo,ops_crypt_t *decrypt, + ops_region_t *region) + { + encrypted_arg_t *arg=ops_mallocz(sizeof *arg); + + arg->decrypt=decrypt; + arg->region=region; + + ops_decrypt_init(arg->decrypt); + + ops_reader_push(pinfo,encrypted_data_reader,encrypted_data_destroyer,arg); + } + +/** + * \ingroup Core_Readers_Encrypted + * \brief Pops decryption reader from stack + * \sa ops_reader_push_decrypt() + */ +void ops_reader_pop_decrypt(ops_parse_info_t *pinfo) + { + encrypted_arg_t *arg=ops_reader_get_arg(ops_parse_get_rinfo(pinfo)); + + arg->decrypt->decrypt_finish(arg->decrypt); + free(arg); + + ops_reader_pop(pinfo); + } + +// eof diff --git a/openpgpsdk/src/reader_encrypted_seip.c b/openpgpsdk/src/reader_encrypted_seip.c new file mode 100644 index 000000000..4df216697 --- /dev/null +++ b/openpgpsdk/src/reader_encrypted_seip.c @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + * \brief Parser for OpenPGP packets + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "parse_local.h" + +#include +#include +#include +#include +#ifndef WIN32 +#include +#endif +#include +#include + +#include + +static int debug=0; + +typedef struct + { + // boolean: false once we've done the preamble/MDC checks + // and are reading from the plaintext + int passed_checks; + unsigned char *plaintext; + size_t plaintext_available; + size_t plaintext_offset; + ops_region_t *region; + ops_crypt_t *decrypt; + } decrypt_se_ip_arg_t; + +static int se_ip_data_reader(void *dest_, size_t len, ops_error_t **errors, + ops_reader_info_t *rinfo, + ops_parse_cb_info_t *cbinfo) + { + + /* + Gets entire SE_IP data packet. + Verifies leading preamble + Verifies trailing MDC packet + Then passes up plaintext as requested + */ + + unsigned int n=0; + + ops_region_t decrypted_region; + + decrypt_se_ip_arg_t *arg=ops_reader_get_arg(rinfo); + + if (!arg->passed_checks) + { + unsigned char*buf=NULL; + + ops_hash_t hash; + unsigned char hashed[SHA_DIGEST_LENGTH]; + + size_t b; + size_t sz_preamble; + size_t sz_mdc_hash; + size_t sz_mdc; + size_t sz_plaintext; + + unsigned char* preamble; + unsigned char* plaintext; + unsigned char* mdc; + unsigned char* mdc_hash; + + ops_hash_any(&hash,OPS_HASH_SHA1); + hash.init(&hash); + + ops_init_subregion(&decrypted_region,NULL); + decrypted_region.length = arg->region->length - arg->region->length_read; + buf=ops_mallocz(decrypted_region.length); + + // read entire SE IP packet + + if (!ops_stacked_limited_read(buf,decrypted_region.length, &decrypted_region,errors,rinfo,cbinfo)) + { + free (buf); + return -1; + } + + if (debug) + { + unsigned int i=0; + fprintf(stderr,"\n\nentire SE IP packet (len=%d):\n",decrypted_region.length); + for (i=0; idecrypt->blocksize+2;i++) + fprintf(stderr," 0x%02x", buf[i]); + fprintf(stderr,"\n"); + } + + b=arg->decrypt->blocksize; + if(buf[b-2] != buf[b] || buf[b-1] != buf[b+1]) + { + fprintf(stderr,"Bad symmetric decrypt (%02x%02x vs %02x%02x)\n", + buf[b-2],buf[b-1],buf[b],buf[b+1]); + OPS_ERROR(errors, OPS_E_PROTO_BAD_SYMMETRIC_DECRYPT,"Bad symmetric decrypt when parsing SE IP packet"); + free(buf); + return -1; + } + + // Verify trailing MDC hash + + sz_preamble=arg->decrypt->blocksize+2; + sz_mdc_hash=OPS_SHA1_HASH_SIZE; + sz_mdc=1+1+sz_mdc_hash; + sz_plaintext=decrypted_region.length-sz_preamble-sz_mdc; + + preamble=buf; + plaintext=buf+sz_preamble; + mdc=plaintext+sz_plaintext; + mdc_hash=mdc+2; + +#ifdef DEBUG + if (debug) + { + unsigned int i=0; + + fprintf(stderr,"\nplaintext (len=%ld): ",sz_plaintext); + for (i=0; iplaintext); + arg->plaintext=ops_mallocz(sz_plaintext); + memcpy(arg->plaintext, plaintext, sz_plaintext); + arg->plaintext_available=sz_plaintext; + + arg->passed_checks=1; + + free(buf); + } + + n=len; + if (n > arg->plaintext_available) + n=arg->plaintext_available; + + memcpy(dest_, arg->plaintext+arg->plaintext_offset, n); + arg->plaintext_available-=n; + arg->plaintext_offset+=n; + len-=n; + + return n; + } + +static void se_ip_data_destroyer(ops_reader_info_t *rinfo) + { + decrypt_se_ip_arg_t* arg=ops_reader_get_arg(rinfo); + free (arg->plaintext); + free (arg); + // free(ops_reader_get_arg(rinfo)); + } + +/** + \ingroup Internal_Readers_SEIP +*/ +void ops_reader_push_se_ip_data(ops_parse_info_t *pinfo, ops_crypt_t *decrypt, + ops_region_t *region) + { + decrypt_se_ip_arg_t *arg=ops_mallocz(sizeof *arg); + arg->region=region; + arg->decrypt=decrypt; + + ops_reader_push(pinfo, se_ip_data_reader, se_ip_data_destroyer,arg); + } + +/** + \ingroup Internal_Readers_SEIP + */ +void ops_reader_pop_se_ip_data(ops_parse_info_t* pinfo) + { + // decrypt_se_ip_arg_t *arg=ops_reader_get_arg(ops_parse_get_rinfo(pinfo)); + // free(arg); + ops_reader_pop(pinfo); + } + +// eof diff --git a/openpgpsdk/src/reader_fd.c b/openpgpsdk/src/reader_fd.c new file mode 100644 index 000000000..8681c65d7 --- /dev/null +++ b/openpgpsdk/src/reader_fd.c @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifndef WIN32 +#include +#endif + +#include + +#include + +/** Arguments for reader_fd + */ +typedef struct + { + int fd; /*!< file descriptor */ + } reader_fd_arg_t; + +/** + * \ingroup Core_Readers + * + * ops_reader_fd() attempts to read up to "plength" bytes from the file + * descriptor in "parse_info" into the buffer starting at "dest" using the + * rules contained in "flags" + * + * \param dest Pointer to previously allocated buffer + * \param plength Number of bytes to try to read + * \param flags Rules about reading to use + * \param parse_info Gets cast to ops_reader_fd_arg_t + * + * \return OPS_R_EOF if no bytes were read + * \return OPS_R_PARTIAL_READ if not enough bytes were read, and OPS_RETURN_LENGTH is set in "flags" + * \return OPS_R_EARLY_EOF if not enough bytes were read, and OPS_RETURN_LENGTH was not set in "flags" + * \return OPS_R_OK if expected length was read + * \return OPS_R_ERROR if cannot read + * + * OPS_R_EARLY_EOF and OPS_R_ERROR push errors on the stack + * + * \sa enum opt_reader_ret_t + * + * \todo change arg_ to typesafe? + */ +static int fd_reader(void *dest,size_t length,ops_error_t **errors, + ops_reader_info_t *rinfo,ops_parse_cb_info_t *cbinfo) + { + reader_fd_arg_t *arg=ops_reader_get_arg(rinfo); + int n=read(arg->fd,dest,length); + + OPS_USED(cbinfo); + + if(n == 0) + return 0; + + if(n < 0) + { + OPS_SYSTEM_ERROR_1(errors,OPS_E_R_READ_FAILED,"read", + "file descriptor %d",arg->fd); + return -1; + } + + return n; + } + +static void fd_destroyer(ops_reader_info_t *rinfo) + { free(ops_reader_get_arg(rinfo)); } + +/** + \ingroup Core_Readers_First + \brief Starts stack with file reader +*/ + +void ops_reader_set_fd(ops_parse_info_t *pinfo,int fd) + { + reader_fd_arg_t *arg=malloc(sizeof *arg); + + arg->fd=fd; + ops_reader_set(pinfo,fd_reader,fd_destroyer,arg); + } + +// eof diff --git a/openpgpsdk/src/reader_hashed.c b/openpgpsdk/src/reader_hashed.c new file mode 100644 index 000000000..3cc6d2bc2 --- /dev/null +++ b/openpgpsdk/src/reader_hashed.c @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + */ + +#include +#include +#include + +#include + +static int hash_reader(void *dest,size_t length,ops_error_t **errors, + ops_reader_info_t *rinfo,ops_parse_cb_info_t *cbinfo) + { + ops_hash_t *hash=ops_reader_get_arg(rinfo); + int r=ops_stacked_read(dest,length,errors,rinfo,cbinfo); + + if(r <= 0) + return r; + + hash->add(hash,dest,r); + + return r; + } + +/** + \ingroup Internal_Readers_Hash + \brief Push hashed data reader on stack +*/ +void ops_reader_push_hash(ops_parse_info_t *pinfo,ops_hash_t *hash) + { + hash->init(hash); + ops_reader_push(pinfo,hash_reader,NULL,hash); + } + +/** + \ingroup Internal_Readers_Hash + \brief Pop hashed data reader from stack +*/ +void ops_reader_pop_hash(ops_parse_info_t *pinfo) + { ops_reader_pop(pinfo); } + +// EOF diff --git a/openpgpsdk/src/reader_mem.c b/openpgpsdk/src/reader_mem.c new file mode 100644 index 000000000..2802673c8 --- /dev/null +++ b/openpgpsdk/src/reader_mem.c @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifndef WIN32 +#include +#endif + +#include + +#include + +typedef struct + { + const unsigned char *buffer; + size_t length; + size_t offset; + } reader_mem_arg_t; + +static int mem_reader(void *dest,size_t length,ops_error_t **errors, + ops_reader_info_t *rinfo,ops_parse_cb_info_t *cbinfo) + { + reader_mem_arg_t *arg=ops_reader_get_arg(rinfo); + unsigned n; + + OPS_USED(cbinfo); + OPS_USED(errors); + + if(arg->offset+length > arg->length) + n=arg->length-arg->offset; + else + n=length; + + if(n == 0) + return 0; + + memcpy(dest,arg->buffer+arg->offset,n); + arg->offset+=n; + + return n; + } + +static void mem_destroyer(ops_reader_info_t *rinfo) + { free(ops_reader_get_arg(rinfo)); } + +/** + \ingroup Core_Readers_First + \brief Starts stack with memory reader +*/ + +void ops_reader_set_memory(ops_parse_info_t *pinfo,const void *buffer, + size_t length) + { + reader_mem_arg_t *arg=malloc(sizeof *arg); + + arg->buffer=buffer; + arg->length=length; + arg->offset=0; + ops_reader_set(pinfo,mem_reader,mem_destroyer,arg); + } + +/* eof */ diff --git a/openpgpsdk/src/readerwriter.c b/openpgpsdk/src/readerwriter.c new file mode 100644 index 000000000..c927a9a4a --- /dev/null +++ b/openpgpsdk/src/readerwriter.c @@ -0,0 +1,451 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#ifndef WIN32 +#include +#else +#include +#endif +#include +#include +#include +#include + +#include +#include + +#include "parse_local.h" + +/** + \ingroup Core_Writers + \brief Create and initialise cinfo and mem; Set for writing to mem + \param cinfo Address where new cinfo pointer will be set + \param mem Address when new mem pointer will be set + \param bufsz Initial buffer size (will automatically be increased when necessary) + \note It is the caller's responsiblity to free cinfo and mem. + \sa ops_teardown_memory_write() +*/ +void ops_setup_memory_write(ops_create_info_t **cinfo, ops_memory_t **mem, size_t bufsz) + { + /* + * initialise needed structures for writing to memory + */ + + *cinfo=ops_create_info_new(); + *mem=ops_memory_new(); + + ops_memory_init(*mem,bufsz); + + ops_writer_set_memory(*cinfo,*mem); + } + +/** + \ingroup Core_Writers + \brief Closes writer and frees cinfo and mem + \param cinfo + \param mem + \sa ops_setup_memory_write() +*/ +void ops_teardown_memory_write(ops_create_info_t *cinfo, ops_memory_t *mem) + { + ops_writer_close(cinfo); // new + ops_create_info_delete(cinfo); + ops_memory_free(mem); + } + +/** + \ingroup Core_Readers + \brief Create parse_info and sets to read from memory + \param pinfo Address where new parse_info will be set + \param mem Memory to read from + \param arg Reader-specific arg + \param callback Callback to use with reader + \param accumulate Set if we need to accumulate as we read. (Usually false unless doing signature verification) + \note It is the caller's responsiblity to free parse_info + \sa ops_teardown_memory_read() +*/ +void ops_setup_memory_read(ops_parse_info_t **pinfo, ops_memory_t *mem, + void* arg, + ops_parse_cb_return_t callback(const ops_parser_content_t *, ops_parse_cb_info_t *), + ops_boolean_t accumulate) + { + /* + * initialise needed uctures for reading + */ + + *pinfo=ops_parse_info_new(); + ops_parse_cb_set(*pinfo,callback,arg); + ops_reader_set_memory(*pinfo, + ops_memory_get_data(mem), + ops_memory_get_length(mem)); + + if (accumulate) + (*pinfo)->rinfo.accumulate=ops_true; + } + +/** + \ingroup Core_Readers + \brief Frees pinfo and mem + \param pinfo + \param mem + \sa ops_setup_memory_read() +*/ +void ops_teardown_memory_read(ops_parse_info_t *pinfo, ops_memory_t *mem) + { + ops_parse_info_delete(pinfo); + ops_memory_free(mem); + } + +/** + \ingroup Core_Writers + \brief Create and initialise cinfo and mem; Set for writing to file + \param cinfo Address where new cinfo pointer will be set + \param filename File to write to + \param allow_overwrite Allows file to be overwritten, if set. + \return Newly-opened file descriptor + \note It is the caller's responsiblity to free cinfo and to close fd. + \sa ops_teardown_file_write() +*/ +int ops_setup_file_write(ops_create_info_t **cinfo, const char* filename, ops_boolean_t allow_overwrite) + { + int fd=0; + int flags=0; + + /* + * initialise needed structures for writing to file + */ + + flags=O_WRONLY | O_CREAT; + if (allow_overwrite==ops_true) + flags |= O_TRUNC; + else + flags |= O_EXCL; + +#ifdef WIN32 + flags |= O_BINARY; +#endif + + fd=open(filename, flags, 0600); + if(fd < 0) + { + perror(filename); + return fd; + } + + *cinfo=ops_create_info_new(); + + ops_writer_set_fd(*cinfo,fd); + + return fd; + } + +/** + \ingroup Core_Writers + \brief Closes writer, frees info, closes fd + \param cinfo + \param fd +*/ +void ops_teardown_file_write(ops_create_info_t *cinfo, int fd) + { + ops_writer_close(cinfo); + close(fd); + ops_create_info_delete(cinfo); + } + +/** + \ingroup Core_Writers + \brief As ops_setup_file_write, but appends to file +*/ +int ops_setup_file_append(ops_create_info_t **cinfo, const char* filename) + { + int fd; + /* + * initialise needed structures for writing to file + */ + +#ifdef WIN32 + fd=open(filename,O_WRONLY | O_APPEND | O_BINARY, 0600); +#else + fd=open(filename,O_WRONLY | O_APPEND, 0600); +#endif + if(fd < 0) + { + perror(filename); + return fd; + } + + *cinfo=ops_create_info_new(); + + ops_writer_set_fd(*cinfo,fd); + + return fd; + } + +/** + \ingroup Core_Writers + \brief As ops_teardown_file_write() +*/ +void ops_teardown_file_append(ops_create_info_t *cinfo, int fd) + { + ops_teardown_file_write(cinfo,fd); + } + +/** + \ingroup Core_Readers + \brief Creates parse_info, opens file, and sets to read from file + \param pinfo Address where new parse_info will be set + \param filename Name of file to read + \param arg Reader-specific arg + \param callback Callback to use when reading + \param accumulate Set if we need to accumulate as we read. (Usually false unless doing signature verification) + \note It is the caller's responsiblity to free parse_info and to close fd + \sa ops_teardown_file_read() +*/ + +int ops_setup_file_read(ops_parse_info_t **pinfo, const char *filename, + void* arg, + ops_parse_cb_return_t callback(const ops_parser_content_t *, ops_parse_cb_info_t *), + ops_boolean_t accumulate) + { + int fd=0; + /* + * initialise needed structures for reading + */ + +#ifdef WIN32 + fd=open(filename,O_RDONLY | O_BINARY); +#else + fd=open(filename,O_RDONLY); +#endif + if (fd < 0) + { + perror(filename); + return fd; + } + + *pinfo=ops_parse_info_new(); + ops_parse_cb_set(*pinfo,callback,arg); + ops_reader_set_fd(*pinfo,fd); + + if (accumulate) + (*pinfo)->rinfo.accumulate=ops_true; + + return fd; + } + +/** + \ingroup Core_Readers + \brief Frees pinfo and closes fd + \param pinfo + \param fd + \sa ops_setup_file_read() +*/ +void ops_teardown_file_read(ops_parse_info_t *pinfo, int fd) + { + close(fd); + ops_parse_info_delete(pinfo); + } + +ops_parse_cb_return_t +callback_literal_data(const ops_parser_content_t *content_,ops_parse_cb_info_t *cbinfo) + { + ops_parser_content_union_t* content=(ops_parser_content_union_t *)&content_->content; + + OPS_USED(cbinfo); + + // ops_print_packet(content_); + + // Read data from packet into static buffer + switch(content_->tag) + { + case OPS_PTAG_CT_LITERAL_DATA_BODY: + // if writer enabled, use it + if (cbinfo->cinfo) + { + ops_write(content->literal_data_body.data, + content->literal_data_body.length, + cbinfo->cinfo); + } + /* + ops_memory_add(mem_literal_data, + content->literal_data_body.data, + content->literal_data_body.length); + */ + break; + + case OPS_PTAG_CT_LITERAL_DATA_HEADER: + // ignore + break; + + default: + // return callback_general(content_,cbinfo); + break; + } + + return OPS_RELEASE_MEMORY; + } + +ops_parse_cb_return_t +callback_pk_session_key(const ops_parser_content_t *content_,ops_parse_cb_info_t *cbinfo) + { + ops_parser_content_union_t* content=(ops_parser_content_union_t *)&content_->content; + + OPS_USED(cbinfo); + + // ops_print_packet(content_); + + // Read data from packet into static buffer + switch(content_->tag) + { + case OPS_PTAG_CT_PK_SESSION_KEY: + // printf ("OPS_PTAG_CT_PK_SESSION_KEY\n"); + assert(cbinfo->cryptinfo.keyring); + cbinfo->cryptinfo.keydata=ops_keyring_find_key_by_id(cbinfo->cryptinfo.keyring, + content->pk_session_key.key_id); + if(!cbinfo->cryptinfo.keydata) + break; + break; + + default: + // return callback_general(content_,cbinfo); + break; + } + + return OPS_RELEASE_MEMORY; + } + +/** + \ingroup Core_Callbacks + +\brief Callback to get secret key, decrypting if necessary. + +@verbatim + This callback does the following: + * finds the session key in the keyring + * gets a passphrase if required + * decrypts the secret key, if necessary + * sets the secret_key in the content struct +@endverbatim +*/ + +ops_parse_cb_return_t +callback_cmd_get_secret_key(const ops_parser_content_t *content_,ops_parse_cb_info_t *cbinfo) + { + ops_parser_content_union_t* content=(ops_parser_content_union_t *)&content_->content; + const ops_secret_key_t *secret; + ops_parser_content_t pc; + + OPS_USED(cbinfo); + +// ops_print_packet(content_); + + switch(content_->tag) + { + case OPS_PARSER_CMD_GET_SECRET_KEY: + cbinfo->cryptinfo.keydata=ops_keyring_find_key_by_id(cbinfo->cryptinfo.keyring,content->get_secret_key.pk_session_key->key_id); + if (!cbinfo->cryptinfo.keydata || !ops_is_key_secret(cbinfo->cryptinfo.keydata)) + return 0; + + /* now get the key from the data */ + secret=ops_get_secret_key_from_data(cbinfo->cryptinfo.keydata); + while(!secret) + { + if (!cbinfo->cryptinfo.passphrase) + { + memset(&pc,'\0',sizeof pc); + pc.content.secret_key_passphrase.passphrase=&cbinfo->cryptinfo.passphrase; + CB(cbinfo,OPS_PARSER_CMD_GET_SK_PASSPHRASE,&pc); + if (!cbinfo->cryptinfo.passphrase) + { + fprintf(stderr,"can't get passphrase\n"); + assert(0); + } + } + /* then it must be encrypted */ + secret=ops_decrypt_secret_key_from_data(cbinfo->cryptinfo.keydata,cbinfo->cryptinfo.passphrase); + } + + *content->get_secret_key.secret_key=secret; + break; + + default: + // return callback_general(content_,cbinfo); + break; + } + + return OPS_RELEASE_MEMORY; + } + +char *ops_get_passphrase(void) + { + return ops_malloc_passphrase(getpass("Passphrase: ")); + } + +char *ops_malloc_passphrase(char *pp) + { + char *passphrase; + size_t n; + + n=strlen(pp); + passphrase=malloc(n+1); + strncpy(passphrase,pp,n+1); + + return passphrase; + } + +/** + \ingroup HighLevel_Callbacks + \brief Callback to use when you need to prompt user for passphrase + \param content_ + \param cbinfo +*/ +ops_parse_cb_return_t +callback_cmd_get_passphrase_from_cmdline(const ops_parser_content_t *content_,ops_parse_cb_info_t *cbinfo) + { + ops_parser_content_union_t* content=(ops_parser_content_union_t *)&content_->content; + + OPS_USED(cbinfo); + +// ops_print_packet(content_); + + switch(content_->tag) + { + case OPS_PARSER_CMD_GET_SK_PASSPHRASE: + *(content->secret_key_passphrase.passphrase)=ops_get_passphrase(); + return OPS_KEEP_MEMORY; + break; + + default: + // return callback_general(content_,cbinfo); + break; + } + + return OPS_RELEASE_MEMORY; + } + +ops_boolean_t ops_reader_set_accumulate(ops_parse_info_t* pinfo, ops_boolean_t state) + { + pinfo->rinfo.accumulate=state; + return state; + } + +// EOF diff --git a/openpgpsdk/src/signature.c b/openpgpsdk/src/signature.c new file mode 100644 index 000000000..2430dc80f --- /dev/null +++ b/openpgpsdk/src/signature.c @@ -0,0 +1,1322 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include + +static int debug=0; +#define MAXBUF 1024 /*info); + sig->info=NULL; + free(sig); + } + +static unsigned char prefix_md5[]={ 0x30,0x20,0x30,0x0C,0x06,0x08,0x2A,0x86, + 0x48,0x86,0xF7,0x0D,0x02,0x05,0x05,0x00, + 0x04,0x10 }; + +static unsigned char prefix_sha1[]={ 0x30,0x21,0x30,0x09,0x06,0x05,0x2b,0x0E, + 0x03,0x02,0x1A,0x05,0x00,0x04,0x14 }; + +static unsigned char prefix_sha256[]={ 0x30,0x31,0x30,0x0d,0x06,0x09,0x60,0x86, + 0x48,0x01,0x65,0x03,0x04,0x02,0x01,0x05, + 0x00,0x04,0x20 }; + +/** + \ingroup Core_Create + implementation of EMSA-PKCS1-v1_5, as defined in OpenPGP RFC + \param M + \param mLen + \param hash_alg Hash algorithm to use + \param EM + \return ops_true if OK; else ops_false +*/ +ops_boolean_t encode_hash_buf(const unsigned char *M, size_t mLen, + const ops_hash_algorithm_t hash_alg, + unsigned char* EM +) + { + // implementation of EMSA-PKCS1-v1_5, as defined in OpenPGP RFC + + unsigned i; + + int n=0; + ops_hash_t hash; + int hash_sz=0; + int encoded_hash_sz=0; + int prefix_sz=0; + unsigned padding_sz=0; + unsigned encoded_msg_sz=0; + unsigned char* prefix=NULL; + + assert(hash_alg == OPS_HASH_SHA1); + + // 1. Apply hash function to M + + ops_hash_any(&hash,hash_alg); + hash.init(&hash); + hash.add(&hash,M,mLen); + + // \todo combine with rsa_sign + + // 2. Get hash prefix + + switch(hash_alg) + { + case OPS_HASH_SHA1: + prefix=prefix_sha1; + prefix_sz=sizeof prefix_sha1; + hash_sz=OPS_SHA1_HASH_SIZE; + encoded_hash_sz=hash_sz+prefix_sz; + // \todo why is Ben using a PS size of 90 in rsa_sign? + // (keysize-hashsize-1-2) + padding_sz=90; + break; + + default: + assert(0); + } + + // \todo 3. Test for len being too short + + // 4 and 5. Generate PS and EM + + EM[0]=0x00; + EM[1]=0x01; + + for (i=0; in)+7)/8; + assert(keysize <= sizeof hashbuf); + assert(10+hashsize <= keysize); + + hashbuf[0]=0; + hashbuf[1]=1; + if (debug) + { printf("rsa_sign: PS is %d\n", keysize-hashsize-1-2); } + for(n=2 ; n < keysize-hashsize-1 ; ++n) + hashbuf[n]=0xff; + hashbuf[n++]=0; + + memcpy(&hashbuf[n],prefix_sha1,sizeof prefix_sha1); + n+=sizeof prefix_sha1; + + t=hash->finish(hash,&hashbuf[n]); + assert(t == 20); + + ops_write(&hashbuf[n],2,opt); + + n+=t; + assert(n == keysize); + + t=ops_rsa_private_encrypt(sigbuf,hashbuf,keysize,srsa,rsa); + bn=BN_bin2bn(sigbuf,t,NULL); + ops_write_mpi(bn,opt); + BN_free(bn); + } + +static void dsa_sign(ops_hash_t *hash, + const ops_dsa_public_key_t *dsa, + const ops_dsa_secret_key_t *sdsa, + ops_create_info_t *cinfo) + { + unsigned char hashbuf[8192]; + unsigned hashsize; + unsigned t; + + // hashsize must be "equal in size to the number of bits of q, + // the group generated by the DSA key's generator value + // 160/8 = 20 + + hashsize=20; + + // finalise hash + t=hash->finish(hash,&hashbuf[0]); + assert(t==20); + + ops_write(&hashbuf[0],2,cinfo); + + // write signature to buf + DSA_SIG* dsasig; + dsasig=ops_dsa_sign(hashbuf,hashsize,sdsa,dsa); + + // convert and write the sig out to memory + ops_write_mpi(dsasig->r,cinfo); + ops_write_mpi(dsasig->s,cinfo); + DSA_SIG_free(dsasig); + } + +static ops_boolean_t rsa_verify(ops_hash_algorithm_t type, + const unsigned char *hash,size_t hash_length, + const ops_rsa_signature_t *sig, + const ops_rsa_public_key_t *rsa) + { + unsigned char sigbuf[8192]; + unsigned char hashbuf_from_sig[8192]; + unsigned n; + unsigned keysize; + unsigned char *prefix; + int plen; + + keysize=BN_num_bytes(rsa->n); + /* RSA key can't be bigger than 65535 bits, so... */ + assert(keysize <= sizeof hashbuf_from_sig); + assert((unsigned)BN_num_bits(sig->sig) <= 8*sizeof sigbuf); + BN_bn2bin(sig->sig,sigbuf); + + n=ops_rsa_public_decrypt(hashbuf_from_sig,sigbuf,(BN_num_bits(sig->sig)+7)/8,rsa); + int debug_len_decrypted=n; + + if(n != keysize) // obviously, this includes error returns + return ops_false; + + // XXX: why is there a leading 0? The first byte should be 1... + // XXX: because the decrypt should use keysize and not sigsize? + if(hashbuf_from_sig[0] != 0 || hashbuf_from_sig[1] != 1) + return ops_false; + + switch(type) + { + case OPS_HASH_MD5: prefix=prefix_md5; plen=sizeof prefix_md5; break; + case OPS_HASH_SHA1: prefix=prefix_sha1; plen=sizeof prefix_sha1; break; + case OPS_HASH_SHA256: prefix=prefix_sha256; plen=sizeof prefix_sha256; break; + default: assert(0); break; + } + + if(keysize-plen-hash_length < 10) + return ops_false; + + for(n=2 ; n < keysize-plen-hash_length-1 ; ++n) + if(hashbuf_from_sig[n] != 0xff) + return ops_false; + + if(hashbuf_from_sig[n++] != 0) + return ops_false; + + if (debug) + { + int zz; + + printf("\n"); + printf("hashbuf_from_sig\n"); + for (zz=0; zzadd(hash,ops_memory_get_data(mem),l); + + ops_memory_free(mem); + } + +static void initialise_hash(ops_hash_t *hash,const ops_signature_t *sig) + { + ops_hash_any(hash,sig->info.hash_algorithm); + hash->init(hash); + } + +static void init_key_signature(ops_hash_t *hash,const ops_signature_t *sig, + const ops_public_key_t *key) + { + initialise_hash(hash,sig); + hash_add_key(hash,key); + } + +static void hash_add_trailer(ops_hash_t *hash,const ops_signature_t *sig, + const unsigned char *raw_packet) + { + if(sig->info.version == OPS_V4) + { + if(raw_packet) + hash->add(hash,raw_packet+sig->v4_hashed_data_start, + sig->info.v4_hashed_data_length); + ops_hash_add_int(hash,sig->info.version,1); + ops_hash_add_int(hash,0xff,1); + ops_hash_add_int(hash,sig->info.v4_hashed_data_length,4); + } + else + { + ops_hash_add_int(hash,sig->info.type,1); + ops_hash_add_int(hash,sig->info.creation_time,4); + } + } + +/** + \ingroup Core_Signature + \brief Checks a signature + \param hash Signature Hash to be checked + \param length Signature Length + \param sig The Signature to be checked + \param signer The signer's public key + \return ops_true if good; else ops_false +*/ +ops_boolean_t ops_check_signature(const unsigned char *hash,unsigned length, + const ops_signature_t *sig, + const ops_public_key_t *signer) + { + ops_boolean_t ret; + + /* + printf(" hash="); + // hashout[0]=0; + hexdump(hash,length); + */ + + switch(sig->info.key_algorithm) + { + case OPS_PKA_DSA: + ret=ops_dsa_verify(hash,length,&sig->info.signature.dsa,&signer->key.dsa); + break; + + case OPS_PKA_RSA: + ret=rsa_verify(sig->info.hash_algorithm,hash,length,&sig->info.signature.rsa, + &signer->key.rsa); + break; + + default: + assert(0); + } + + return ret; + } + +static ops_boolean_t hash_and_check_signature(ops_hash_t *hash, + const ops_signature_t *sig, + const ops_public_key_t *signer) + { + int n; + unsigned char hashout[OPS_MAX_HASH_SIZE]; + + n=hash->finish(hash,hashout); + + return ops_check_signature(hashout,n,sig,signer); + } + +static ops_boolean_t finalise_signature(ops_hash_t *hash, + const ops_signature_t *sig, + const ops_public_key_t *signer, + const unsigned char *raw_packet) + { + hash_add_trailer(hash,sig,raw_packet); + return hash_and_check_signature(hash,sig,signer); + } + +/** + * \ingroup Core_Signature + * + * \brief Verify a certification signature. + * + * \param key The public key that was signed. + * \param id The user ID that was signed + * \param sig The signature. + * \param signer The public key of the signer. + * \param raw_packet The raw signature packet. + * \return ops_true if OK; else ops_false + */ +ops_boolean_t +ops_check_user_id_certification_signature(const ops_public_key_t *key, + const ops_user_id_t *id, + const ops_signature_t *sig, + const ops_public_key_t *signer, + const unsigned char *raw_packet) + { + ops_hash_t hash; + size_t user_id_len=strlen((char *)id->user_id); + + init_key_signature(&hash,sig,key); + + if(sig->info.version == OPS_V4) + { + ops_hash_add_int(&hash,0xb4,1); + ops_hash_add_int(&hash,user_id_len,4); + } + hash.add(&hash,id->user_id,user_id_len); + + return finalise_signature(&hash,sig,signer,raw_packet); + } + +/** + * \ingroup Core_Signature + * + * Verify a certification signature. + * + * \param key The public key that was signed. + * \param attribute The user attribute that was signed + * \param sig The signature. + * \param signer The public key of the signer. + * \param raw_packet The raw signature packet. + * \return ops_true if OK; else ops_false + */ +ops_boolean_t +ops_check_user_attribute_certification_signature(const ops_public_key_t *key, + const ops_user_attribute_t *attribute, + const ops_signature_t *sig, + const ops_public_key_t *signer, + const unsigned char *raw_packet) + { + ops_hash_t hash; + + init_key_signature(&hash,sig,key); + + if(sig->info.version == OPS_V4) + { + ops_hash_add_int(&hash,0xd1,1); + ops_hash_add_int(&hash,attribute->data.len,4); + } + hash.add(&hash,attribute->data.contents,attribute->data.len); + + return finalise_signature(&hash,sig,signer,raw_packet); + } + +/** + * \ingroup Core_Signature + * + * Verify a subkey signature. + * + * \param key The public key whose subkey was signed. + * \param subkey The subkey of the public key that was signed. + * \param sig The signature. + * \param signer The public key of the signer. + * \param raw_packet The raw signature packet. + * \return ops_true if OK; else ops_false + */ +ops_boolean_t +ops_check_subkey_signature(const ops_public_key_t *key, + const ops_public_key_t *subkey, + const ops_signature_t *sig, + const ops_public_key_t *signer, + const unsigned char *raw_packet) + { + ops_hash_t hash; + + init_key_signature(&hash,sig,key); + hash_add_key(&hash,subkey); + + return finalise_signature(&hash,sig,signer,raw_packet); + } + +/** + * \ingroup Core_Signature + * + * Verify a direct signature. + * + * \param key The public key which was signed. + * \param sig The signature. + * \param signer The public key of the signer. + * \param raw_packet The raw signature packet. + * \return ops_true if OK; else ops_false + */ +ops_boolean_t +ops_check_direct_signature(const ops_public_key_t *key, + const ops_signature_t *sig, + const ops_public_key_t *signer, + const unsigned char *raw_packet) + { + ops_hash_t hash; + + init_key_signature(&hash,sig,key); + return finalise_signature(&hash,sig,signer,raw_packet); + } + +/** + * \ingroup Core_Signature + * + * Verify a signature on a hash (the hash will have already been fed + * the material that was being signed, for example signed cleartext). + * + * \param hash A hash structure of appropriate type that has been fed + * the material to be signed. This MUST NOT have been finalised. + * \param sig The signature to be verified. + * \param signer The public key of the signer. + * \return ops_true if OK; else ops_false + */ +ops_boolean_t +ops_check_hash_signature(ops_hash_t *hash, + const ops_signature_t *sig, + const ops_public_key_t *signer) + { + if(sig->info.hash_algorithm != hash->algorithm) + return ops_false; + + return finalise_signature(hash,sig,signer,NULL); + } + +static void start_signature_in_mem(ops_create_signature_t *sig) + { + // since this has subpackets and stuff, we have to buffer the whole + // thing to get counts before writing. + sig->mem=ops_memory_new(); + ops_memory_init(sig->mem,100); + ops_writer_set_memory(sig->info,sig->mem); + + // write nearly up to the first subpacket + ops_write_scalar(sig->sig.info.version,1,sig->info); + ops_write_scalar(sig->sig.info.type,1,sig->info); + ops_write_scalar(sig->sig.info.key_algorithm,1,sig->info); + ops_write_scalar(sig->sig.info.hash_algorithm,1,sig->info); + + // dummy hashed subpacket count + sig->hashed_count_offset=ops_memory_get_length(sig->mem); + ops_write_scalar(0,2,sig->info); + } + +/** + * \ingroup Core_Signature + * + * ops_signature_start() creates a V4 public key signature with a SHA1 hash. + * + * \param sig The signature structure to initialise + * \param key The public key to be signed + * \param id The user ID being bound to the key + * \param type Signature type + */ +void ops_signature_start_key_signature(ops_create_signature_t *sig, + const ops_public_key_t *key, + const ops_user_id_t *id, + ops_sig_type_t type) + { + sig->info=ops_create_info_new(); + + // XXX: refactor with check (in several ways - check should probably + // use the buffered writer to construct packets (done), and also should + // share code for hash calculation) + sig->sig.info.version=OPS_V4; + sig->sig.info.hash_algorithm=OPS_HASH_SHA1; + sig->sig.info.key_algorithm=key->algorithm; + sig->sig.info.type=type; + + sig->hashed_data_length=-1; + + init_key_signature(&sig->hash,&sig->sig,key); + + ops_hash_add_int(&sig->hash,0xb4,1); + ops_hash_add_int(&sig->hash,strlen((char *)id->user_id),4); + sig->hash.add(&sig->hash,id->user_id,strlen((char *)id->user_id)); + + start_signature_in_mem(sig); + } + +/** + * \ingroup Core_Signature + * + * Create a V4 public key signature over some cleartext. + * + * \param sig The signature structure to initialise + * \param id + * \param type + * \todo Expand description. Allow other hashes. + */ + +static void ops_signature_start_signature(ops_create_signature_t *sig, + const ops_secret_key_t *key, + const ops_hash_algorithm_t hash, + const ops_sig_type_t type) + { + sig->info=ops_create_info_new(); + + // XXX: refactor with check (in several ways - check should probably + // use the buffered writer to construct packets (done), and also should + // share code for hash calculation) + sig->sig.info.version=OPS_V4; + sig->sig.info.key_algorithm=key->public_key.algorithm; + sig->sig.info.hash_algorithm=hash; + sig->sig.info.type=type; + + sig->hashed_data_length=-1; + + if (debug) + { fprintf(stderr,"initialising hash for sig in mem\n"); } + initialise_hash(&sig->hash,&sig->sig); + start_signature_in_mem(sig); + } + +/** + * \ingroup Core_Signature + * \brief Setup to start a cleartext's signature + */ +void ops_signature_start_cleartext_signature(ops_create_signature_t *sig, + const ops_secret_key_t *key, + const ops_hash_algorithm_t hash, + const ops_sig_type_t type) + { + ops_signature_start_signature(sig,key,hash,type); + } + +/** + * \ingroup Core_Signature + * \brief Setup to start a message's signature + */ +void ops_signature_start_message_signature(ops_create_signature_t *sig, + const ops_secret_key_t *key, + const ops_hash_algorithm_t hash, + const ops_sig_type_t type) + { + ops_signature_start_signature(sig,key,hash,type); + } + +/** + * \ingroup Core_Signature + * + * Add plaintext data to a signature-to-be. + * + * \param sig The signature-to-be. + * \param buf The plaintext data. + * \param length The amount of plaintext data. + */ +void ops_signature_add_data(ops_create_signature_t *sig,const void *buf, + size_t length) + { + if (debug) + { fprintf(stderr,"ops_signature_add_data adds to hash\n"); } + sig->hash.add(&sig->hash,buf,length); + } + +/** + * \ingroup Core_Signature + * + * Mark the end of the hashed subpackets in the signature + * + * \param sig + */ + +ops_boolean_t ops_signature_hashed_subpackets_end(ops_create_signature_t *sig) + { + sig->hashed_data_length=ops_memory_get_length(sig->mem) + -sig->hashed_count_offset-2; + ops_memory_place_int(sig->mem,sig->hashed_count_offset, + sig->hashed_data_length,2); + // dummy unhashed subpacket count + sig->unhashed_count_offset=ops_memory_get_length(sig->mem); + return ops_write_scalar(0,2,sig->info); + } + +/** + * \ingroup Core_Signature + * + * Write out a signature + * + * \param sig + * \param key + * \param skey + * \param info + * + */ + +ops_boolean_t ops_write_signature(ops_create_signature_t *sig, const ops_public_key_t *key, + const ops_secret_key_t *skey, ops_create_info_t *info) + { + ops_boolean_t rtn=ops_false; + size_t l=ops_memory_get_length(sig->mem); + + // check key not decrypted + switch (skey->public_key.algorithm) + { + case OPS_PKA_RSA: + case OPS_PKA_RSA_ENCRYPT_ONLY: + case OPS_PKA_RSA_SIGN_ONLY: + assert(skey->key.rsa.d); + break; + + case OPS_PKA_DSA: + assert(skey->key.dsa.x); + break; + + default: + fprintf(stderr,"Unsupported algorithm %d\n", skey->public_key.algorithm); + assert(0); + } + + assert(sig->hashed_data_length != (unsigned)-1); + + ops_memory_place_int(sig->mem,sig->unhashed_count_offset, + l-sig->unhashed_count_offset-2,2); + + // add the packet from version number to end of hashed subpackets + + if (debug) + { fprintf(stderr, "--- Adding packet to hash from version number to hashed subpkts\n"); } + + sig->hash.add(&sig->hash,ops_memory_get_data(sig->mem), + sig->unhashed_count_offset); + + // add final trailer + ops_hash_add_int(&sig->hash,sig->sig.info.version,1); + ops_hash_add_int(&sig->hash,0xff,1); + // +6 for version, type, pk alg, hash alg, hashed subpacket length + ops_hash_add_int(&sig->hash,sig->hashed_data_length+6,4); + + if (debug) + { fprintf(stderr, "--- Finished adding packet to hash from version number to hashed subpkts\n"); } + + // XXX: technically, we could figure out how big the signature is + // and write it directly to the output instead of via memory. + switch(skey->public_key.algorithm) + { + case OPS_PKA_RSA: + case OPS_PKA_RSA_ENCRYPT_ONLY: + case OPS_PKA_RSA_SIGN_ONLY: + rsa_sign(&sig->hash,&key->key.rsa,&skey->key.rsa,sig->info); + break; + + case OPS_PKA_DSA: + dsa_sign(&sig->hash,&key->key.dsa,&skey->key.dsa,sig->info); + break; + + default: + fprintf(stderr,"Unsupported algorithm %d\n", skey->public_key.algorithm); + assert(0); + } + + + + rtn=ops_write_ptag(OPS_PTAG_CT_SIGNATURE,info); + if (rtn!=ops_false) + { + l=ops_memory_get_length(sig->mem); + rtn = ops_write_length(l,info) + && ops_write(ops_memory_get_data(sig->mem),l,info); + } + + ops_memory_free(sig->mem); + + if (rtn==ops_false) + { + OPS_ERROR(&info->errors,OPS_E_W,"Cannot write signature"); + } + return rtn; + } + +/** + * \ingroup Core_Signature + * + * ops_signature_add_creation_time() adds a creation time to the signature. + * + * \param sig + * \param when + */ +ops_boolean_t ops_signature_add_creation_time(ops_create_signature_t *sig,time_t when) + { + return ops_write_ss_header(5,OPS_PTAG_SS_CREATION_TIME,sig->info) + && ops_write_scalar(when,4,sig->info); + } + +/** + * \ingroup Core_Signature + * + * Adds issuer's key ID to the signature + * + * \param sig + * \param keyid + */ + +ops_boolean_t ops_signature_add_issuer_key_id(ops_create_signature_t *sig, + const unsigned char keyid[OPS_KEY_ID_SIZE]) + { + return ops_write_ss_header(OPS_KEY_ID_SIZE+1,OPS_PTAG_SS_ISSUER_KEY_ID,sig->info) + && ops_write(keyid,OPS_KEY_ID_SIZE,sig->info); + } + +/** + * \ingroup Core_Signature + * + * Adds primary user ID to the signature + * + * \param sig + * \param primary + */ +void ops_signature_add_primary_user_id(ops_create_signature_t *sig, + ops_boolean_t primary) + { + ops_write_ss_header(2,OPS_PTAG_SS_PRIMARY_USER_ID,sig->info); + ops_write_scalar(primary,1,sig->info); + } + +/** + * \ingroup Core_Signature + * + * Get the hash structure in use for the signature. + * + * \param sig The signature structure. + * \return The hash structure. + */ +ops_hash_t *ops_signature_get_hash(ops_create_signature_t *sig) + { return &sig->hash; } + +static int open_output_file(ops_create_info_t **cinfo, const char* input_filename, const char* output_filename, const ops_boolean_t use_armour, const ops_boolean_t overwrite) + { + int fd_out; + + // setup output file + + if (output_filename) + { + fd_out=ops_setup_file_write(cinfo, output_filename, overwrite); + } + else + { + char *myfilename=NULL; + unsigned filenamelen=strlen(input_filename)+4+1; + myfilename=ops_mallocz(filenamelen); + if (use_armour) + snprintf(myfilename,filenamelen,"%s.asc",input_filename); + else + snprintf(myfilename,filenamelen,"%s.gpg",input_filename); + fd_out=ops_setup_file_write(cinfo, myfilename, overwrite); + free(myfilename); + } + + return fd_out; + } + +/** + \ingroup HighLevel_Sign + \brief Sign a file with a Cleartext Signature + \param input_filename Name of file to be signed + \param output_filename Filename to be created. If NULL, filename will be constructed from the input_filename. + \param skey Secret Key to sign with + \param overwrite Allow output file to be overwritten, if set + \return ops_true if OK, else ops_false + + Example code: + \code + void example(const ops_secret_key_t *skey, ops_boolean_t overwrite) + { + if (ops_sign_file_as_cleartext("mytestfile.txt",NULL,skey,overwrite)==ops_true) + printf("OK"); + else + printf("ERR"); + } + \endcode +*/ +ops_boolean_t ops_sign_file_as_cleartext(const char* input_filename, const char* output_filename, const ops_secret_key_t *skey, const ops_boolean_t overwrite) + { + // \todo allow choice of hash algorithams + // enforce use of SHA1 for now + + unsigned char keyid[OPS_KEY_ID_SIZE]; + ops_create_signature_t *sig=NULL; + + int fd_in=0; + int fd_out=0; + ops_create_info_t *cinfo=NULL; + unsigned char buf[MAXBUF]; + //int flags=0; + ops_boolean_t rtn=ops_false; + ops_boolean_t use_armour=ops_true; + + // open file to sign +#ifdef WIN32 + fd_in=open(input_filename,O_RDONLY | O_BINARY); +#else + fd_in=open(input_filename,O_RDONLY); +#endif + if(fd_in < 0) + { + return ops_false; + } + + // set up output file + + fd_out=open_output_file(&cinfo, input_filename, output_filename, use_armour, overwrite); + + if (fd_out < 0) + { + close(fd_in); + return ops_false; + } + + // set up signature + sig=ops_create_signature_new(); + if (!sig) + { + close (fd_in); + ops_teardown_file_write(cinfo,fd_out); + return ops_false; + } + + // \todo could add more error detection here + ops_signature_start_cleartext_signature(sig,skey,OPS_HASH_SHA1,OPS_SIG_BINARY); + if (ops_writer_push_clearsigned(cinfo,sig)!=ops_true) + { return ops_false; } + + // Do the signing + + for (;;) + { + int n=0; + + n=read(fd_in,buf,sizeof(buf)); + if (!n) + break; + assert(n>=0); + ops_write(buf,n,cinfo); + } + close(fd_in); + + // add signature with subpackets: + // - creation time + // - key id + rtn = ops_writer_switch_to_armoured_signature(cinfo) + && ops_signature_add_creation_time(sig,time(NULL)); + if (rtn==ops_false) + { + ops_teardown_file_write(cinfo,fd_out); + return ops_false; + } + + ops_keyid(keyid,&skey->public_key); + + rtn = ops_signature_add_issuer_key_id(sig,keyid) + && ops_signature_hashed_subpackets_end(sig) + && ops_write_signature(sig,&skey->public_key,skey,cinfo); + + ops_teardown_file_write(cinfo,fd_out); + + if (rtn==ops_false) + { + OPS_ERROR(&cinfo->errors,OPS_E_W,"Cannot sign file as cleartext"); + } + return rtn; + } + + +/** + * \ingroup HighLevel_Sign + * \brief Sign a buffer with a Cleartext signature + * \param cleartext Text to be signed + * \param len Length of text + * \param signed_cleartext ops_memory_t struct in which to write the signed cleartext + * \param skey Secret key with which to sign the cleartext + * \return ops_true if OK; else ops_false + + * \note It is the calling function's responsibility to free signed_cleartext + * \note signed_cleartext should be a NULL pointer when passed in + + Example code: + \code + void example(const ops_secret_key_t *skey) + { + ops_memory_t* mem=NULL; + const char* buf="Some example text"; + size_t len=strlen(buf); + if (ops_sign_buf_as_cleartext(buf,len, &mem, skey)==ops_true) + printf("OK"); + else + printf("ERR"); + // free signed cleartext after use + ops_memory_free(mem); + } + \endcode + */ +ops_boolean_t ops_sign_buf_as_cleartext(const char* cleartext, const size_t len, ops_memory_t** signed_cleartext, const ops_secret_key_t *skey) + { + ops_boolean_t rtn=ops_false; + + // \todo allow choice of hash algorithams + // enforce use of SHA1 for now + + unsigned char keyid[OPS_KEY_ID_SIZE]; + ops_create_signature_t *sig=NULL; + + ops_create_info_t *cinfo=NULL; + + assert(*signed_cleartext==NULL); + + // set up signature + sig=ops_create_signature_new(); + if (!sig) + { + return ops_false; + } + + // \todo could add more error detection here + ops_signature_start_cleartext_signature(sig,skey,OPS_HASH_SHA1,OPS_SIG_BINARY); + + // set up output file + ops_setup_memory_write(&cinfo, signed_cleartext, len); + + // Do the signing + // add signature with subpackets: + // - creation time + // - key id + rtn = ops_writer_push_clearsigned(cinfo,sig) + && ops_write(cleartext,len,cinfo) + && ops_writer_switch_to_armoured_signature(cinfo) + && ops_signature_add_creation_time(sig,time(NULL)); + + if (rtn==ops_false) + { + return ops_false; + } + + ops_keyid(keyid,&skey->public_key); + + rtn = ops_signature_add_issuer_key_id(sig,keyid) + && ops_signature_hashed_subpackets_end(sig) + && ops_write_signature(sig,&skey->public_key,skey,cinfo) + && ops_writer_close(cinfo); + + // Note: the calling function must free signed_cleartext + ops_create_info_delete(cinfo); + + return rtn; + } + +/** +\ingroup HighLevel_Sign +\brief Sign a file +\param input_filename Input filename +\param output_filename Output filename. If NULL, a name is constructed from the input filename. +\param skey Secret Key to use for signing +\param use_armour Write armoured text, if set. +\param overwrite May overwrite existing file, if set. +\return ops_true if OK; else ops_false; + +Example code: +\code +void example(const ops_secret_key_t *skey) +{ + const char* filename="mytestfile"; + const ops_boolean_t use_armour=ops_false; + const ops_boolean_t overwrite=ops_false; + if (ops_sign_file(filename, NULL, skey, use_armour, overwrite)==ops_true) + printf("OK"); + else + printf("ERR"); +} +\endcode +*/ +ops_boolean_t ops_sign_file(const char* input_filename, const char* output_filename, const ops_secret_key_t *skey, const ops_boolean_t use_armour, const ops_boolean_t overwrite) + { + // \todo allow choice of hash algorithams + // enforce use of SHA1 for now + + unsigned char keyid[OPS_KEY_ID_SIZE]; + ops_create_signature_t *sig=NULL; + + int fd_out=0; + ops_create_info_t *cinfo=NULL; + + ops_hash_algorithm_t hash_alg=OPS_HASH_SHA1; + ops_sig_type_t sig_type=OPS_SIG_BINARY; + + ops_memory_t* mem_buf=NULL; + ops_hash_t* hash=NULL; + + // read input file into buf + + int errnum; + mem_buf=ops_write_mem_from_file(input_filename,&errnum); + if (errnum) + return ops_false; + + // setup output file + + fd_out=open_output_file(&cinfo, input_filename, output_filename, use_armour, overwrite); + + if (fd_out < 0) + { + ops_memory_free(mem_buf); + return ops_false; + } + + // set up signature + sig=ops_create_signature_new(); + ops_signature_start_message_signature(sig, skey, hash_alg, sig_type); + + // set armoured/not armoured here + if (use_armour) + ops_writer_push_armoured_message(cinfo); + + if (debug) + { fprintf(stderr, "** Writing out one pass sig\n"); } + + // write one_pass_sig + ops_write_one_pass_sig(skey, hash_alg, sig_type, cinfo); + + // hash file contents + hash=ops_signature_get_hash(sig); + hash->add(hash, ops_memory_get_data(mem_buf), ops_memory_get_length(mem_buf)); + + // output file contents as Literal Data packet + + if (debug) + { fprintf(stderr,"** Writing out data now\n"); } + + ops_write_literal_data_from_buf(ops_memory_get_data(mem_buf), ops_memory_get_length(mem_buf), OPS_LDT_BINARY, cinfo); + + if (debug) + { fprintf(stderr,"** After Writing out data now\n");} + + // add subpackets to signature + // - creation time + // - key id + + ops_signature_add_creation_time(sig,time(NULL)); + + ops_keyid(keyid,&skey->public_key); + ops_signature_add_issuer_key_id(sig,keyid); + + ops_signature_hashed_subpackets_end(sig); + + // write out sig + ops_write_signature(sig,&skey->public_key,skey,cinfo); + + ops_teardown_file_write(cinfo, fd_out); + + // tidy up + ops_create_signature_delete(sig); + ops_memory_free(mem_buf); + + return ops_true; + } + +/** +\ingroup HighLevel_Sign +\brief Signs a buffer +\param input Input text to be signed +\param input_len Length of input text +\param sig_type Signature type +\param skey Secret Key +\param use_armour Write armoured text, if set +\return New ops_memory_t struct containing signed text +\note It is the caller's responsibility to call ops_memory_free(me) + +Example Code: +\code +void example(const ops_secret_key_t *skey) +{ + const char* buf="Some example text"; + const size_t len=strlen(buf); + const ops_boolean_t use_armour=ops_true; + + ops_memory_t* mem=NULL; + + mem=ops_sign_buf(buf,len,OPS_SIG_BINARY,skey,use_armour); + if (mem) + { + printf ("OK"); + ops_memory_free(mem); + } + else + { + printf("ERR"); + } +} +\endcode +*/ +ops_memory_t* ops_sign_buf(const void* input, const size_t input_len, const ops_sig_type_t sig_type, const ops_secret_key_t *skey, const ops_boolean_t use_armour) + { + // \todo allow choice of hash algorithams + // enforce use of SHA1 for now + + unsigned char keyid[OPS_KEY_ID_SIZE]; + ops_create_signature_t *sig=NULL; + + ops_create_info_t *cinfo=NULL; + ops_memory_t *mem=ops_memory_new(); + + ops_hash_algorithm_t hash_alg=OPS_HASH_SHA1; + ops_literal_data_type_t ld_type; + ops_hash_t* hash=NULL; + + // setup literal data packet type + if (sig_type==OPS_SIG_BINARY) + ld_type=OPS_LDT_BINARY; + else + ld_type=OPS_LDT_TEXT; + + // set up signature + sig=ops_create_signature_new(); + ops_signature_start_message_signature(sig, skey, hash_alg, sig_type); + + // setup writer + ops_setup_memory_write(&cinfo, &mem, input_len); + + // set armoured/not armoured here + if (use_armour) + ops_writer_push_armoured_message(cinfo); + + if (debug) + { fprintf(stderr, "** Writing out one pass sig\n"); } + + // write one_pass_sig + ops_write_one_pass_sig(skey, hash_alg, sig_type, cinfo); + + // hash file contents + hash=ops_signature_get_hash(sig); + hash->add(hash, input, input_len); + + // output file contents as Literal Data packet + + if (debug) + { fprintf(stderr,"** Writing out data now\n"); } + + ops_write_literal_data_from_buf(input, input_len, ld_type, cinfo); + + if (debug) + { fprintf(stderr,"** After Writing out data now\n");} + + // add subpackets to signature + // - creation time + // - key id + + ops_signature_add_creation_time(sig,time(NULL)); + + ops_keyid(keyid,&skey->public_key); + ops_signature_add_issuer_key_id(sig,keyid); + + ops_signature_hashed_subpackets_end(sig); + + // write out sig + ops_write_signature(sig,&skey->public_key,skey,cinfo); + + // tidy up + ops_writer_close(cinfo); + ops_create_signature_delete(sig); + + return mem; + } + +// EOF diff --git a/openpgpsdk/src/src.pro b/openpgpsdk/src/src.pro new file mode 100644 index 000000000..3322088bd --- /dev/null +++ b/openpgpsdk/src/src.pro @@ -0,0 +1,49 @@ +TEMPLATE = lib +CONFIG = staticlib + +DEFINES *= OPENSSL_NO_IDEA + +QMAKE_CXXFLAGS *= -Wall -Werror -W + +TARGET = ops +DESTDIR = ../lib +DEPENDPATH += . +INCLUDEPATH += . ../include + +# Input +HEADERS += keyring_local.h parse_local.h +SOURCES += accumulate.c \ + compress.c \ + create.c \ + crypto.c \ + errors.c \ + fingerprint.c \ + hash.c \ + keyring.c \ + lists.c \ + memory.c \ + openssl_crypto.c \ + packet-parse.c \ + packet-print.c \ + packet-show.c \ + random.c \ + reader.c \ + reader_armoured.c \ + reader_encrypted_se.c \ + reader_encrypted_seip.c \ + reader_fd.c \ + reader_hashed.c \ + reader_mem.c \ + readerwriter.c \ + signature.c \ + symmetric.c \ + util.c \ + validate.c \ + writer.c \ + writer_armour.c \ + writer_encrypt.c \ + writer_encrypt_se_ip.c \ + writer_fd.c \ + writer_memory.c \ + writer_skey_checksum.c \ + writer_stream_encrypt_se_ip.c diff --git a/openpgpsdk/src/symmetric.c b/openpgpsdk/src/symmetric.c new file mode 100644 index 000000000..9b37e595f --- /dev/null +++ b/openpgpsdk/src/symmetric.c @@ -0,0 +1,525 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#ifndef OPENSSL_NO_IDEA +#include +#endif +#include +#include +#include "parse_local.h" + +#include +#include + +//static int debug=0; + +#ifndef ATTRIBUTE_UNUSED + +#ifndef WIN32 +#define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +#else +#define ATTRIBUTE_UNUSED +#endif // #ifndef WIN32 + +#endif /* ATTRIBUTE_UNUSED */ + +static void std_set_iv(ops_crypt_t *crypt,const unsigned char *iv) + { + memcpy(crypt->iv,iv,crypt->blocksize); + crypt->num=0; + } + +static void std_set_key(ops_crypt_t *crypt,const unsigned char *key) + { memcpy(crypt->key,key,crypt->keysize); } + +static void std_resync(ops_crypt_t *decrypt) + { + if(decrypt->num == decrypt->blocksize) + return; + + memmove(decrypt->civ+decrypt->blocksize-decrypt->num,decrypt->civ, + decrypt->num); + memcpy(decrypt->civ,decrypt->siv+decrypt->num, + decrypt->blocksize-decrypt->num); + decrypt->num=0; + } + +static void std_finish(ops_crypt_t *crypt) + { + if (crypt->encrypt_key) + { + free(crypt->encrypt_key); + crypt->encrypt_key=NULL; + } + if (crypt->decrypt_key) + { + free(crypt->decrypt_key); + crypt->decrypt_key=NULL; + } + } + +static void cast5_init(ops_crypt_t *crypt) + { + if (crypt->encrypt_key) + free(crypt->encrypt_key); + crypt->encrypt_key=malloc(sizeof(CAST_KEY)); + CAST_set_key(crypt->encrypt_key,crypt->keysize,crypt->key); + crypt->decrypt_key=malloc(sizeof(CAST_KEY)); + CAST_set_key(crypt->decrypt_key,crypt->keysize,crypt->key); + } + +static void cast5_block_encrypt(ops_crypt_t *crypt,void *out,const void *in) + { CAST_ecb_encrypt(in,out,crypt->encrypt_key,CAST_ENCRYPT); } + +static void cast5_block_decrypt(ops_crypt_t *crypt,void *out,const void *in) + { CAST_ecb_encrypt(in,out,crypt->encrypt_key,CAST_DECRYPT); } + +static void cast5_cfb_encrypt(ops_crypt_t *crypt,void *out,const void *in, size_t count) + { + CAST_cfb64_encrypt(in,out,count, + crypt->encrypt_key, crypt->iv, (int *)&crypt->num, + CAST_ENCRYPT); + } + +static void cast5_cfb_decrypt(ops_crypt_t *crypt,void *out,const void *in, size_t count) + { + CAST_cfb64_encrypt(in,out,count, + crypt->encrypt_key, crypt->iv, (int *)&crypt->num, + CAST_DECRYPT); + } + +#define TRAILER "","","","",0,NULL,NULL + +static ops_crypt_t cast5= + { + OPS_SA_CAST5, + CAST_BLOCK, + CAST_KEY_LENGTH, + std_set_iv, + std_set_key, + cast5_init, + std_resync, + cast5_block_encrypt, + cast5_block_decrypt, + cast5_cfb_encrypt, + cast5_cfb_decrypt, + std_finish, + TRAILER + }; + +#ifndef OPENSSL_NO_IDEA +static void idea_init(ops_crypt_t *crypt) + { + assert(crypt->keysize == IDEA_KEY_LENGTH); + + if (crypt->encrypt_key) + free(crypt->encrypt_key); + crypt->encrypt_key=malloc(sizeof(IDEA_KEY_SCHEDULE)); + + // note that we don't invert the key when decrypting for CFB mode + idea_set_encrypt_key(crypt->key,crypt->encrypt_key); + + if (crypt->decrypt_key) + free(crypt->decrypt_key); + crypt->decrypt_key=malloc(sizeof(IDEA_KEY_SCHEDULE)); + + idea_set_decrypt_key(crypt->encrypt_key,crypt->decrypt_key); + } + +static void idea_block_encrypt(ops_crypt_t *crypt,void *out,const void *in) + { idea_ecb_encrypt(in,out,crypt->encrypt_key); } + +static void idea_block_decrypt(ops_crypt_t *crypt,void *out,const void *in) + { idea_ecb_encrypt(in,out,crypt->decrypt_key); } + +static void idea_cfb_encrypt(ops_crypt_t *crypt,void *out,const void *in, size_t count) + { + idea_cfb64_encrypt(in,out,count, + crypt->encrypt_key, crypt->iv, (int *)&crypt->num, + CAST_ENCRYPT); + } + +static void idea_cfb_decrypt(ops_crypt_t *crypt,void *out,const void *in, size_t count) + { + idea_cfb64_encrypt(in,out,count, + crypt->decrypt_key, crypt->iv, (int *)&crypt->num, + CAST_DECRYPT); + } + +static const ops_crypt_t idea= + { + OPS_SA_IDEA, + IDEA_BLOCK, + IDEA_KEY_LENGTH, + std_set_iv, + std_set_key, + idea_init, + std_resync, + idea_block_encrypt, + idea_block_decrypt, + idea_cfb_encrypt, + idea_cfb_decrypt, + std_finish, + TRAILER + }; +#endif /* OPENSSL_NO_IDEA */ + +// AES with 128-bit key (AES) + +#define KEYBITS_AES128 128 + +static void aes128_init(ops_crypt_t *crypt) + { + if (crypt->encrypt_key) + free(crypt->encrypt_key); + crypt->encrypt_key=malloc(sizeof(AES_KEY)); + if (AES_set_encrypt_key(crypt->key,KEYBITS_AES128,crypt->encrypt_key)) + fprintf(stderr,"aes128_init: Error setting encrypt_key\n"); + + if (crypt->decrypt_key) + free(crypt->decrypt_key); + crypt->decrypt_key=malloc(sizeof(AES_KEY)); + if (AES_set_decrypt_key(crypt->key,KEYBITS_AES128,crypt->decrypt_key)) + fprintf(stderr,"aes128_init: Error setting decrypt_key\n"); + } + +static void aes_block_encrypt(ops_crypt_t *crypt,void *out,const void *in) + { AES_encrypt(in,out,crypt->encrypt_key); } + +static void aes_block_decrypt(ops_crypt_t *crypt,void *out,const void *in) + { AES_decrypt(in,out,crypt->decrypt_key); } + +static void aes_cfb_encrypt(ops_crypt_t *crypt,void *out,const void *in, size_t count) + { + AES_cfb128_encrypt(in,out,count, + crypt->encrypt_key, crypt->iv, (int *)&crypt->num, + AES_ENCRYPT); + } + +static void aes_cfb_decrypt(ops_crypt_t *crypt,void *out,const void *in, size_t count) + { + AES_cfb128_encrypt(in,out,count, + crypt->encrypt_key, crypt->iv, (int *)&crypt->num, + AES_DECRYPT); + } + +static const ops_crypt_t aes128= + { + OPS_SA_AES_128, + AES_BLOCK_SIZE, + KEYBITS_AES128/8, + std_set_iv, + std_set_key, + aes128_init, + std_resync, + aes_block_encrypt, + aes_block_decrypt, + aes_cfb_encrypt, + aes_cfb_decrypt, + std_finish, + TRAILER + }; + +// AES with 256-bit key + +#define KEYBITS_AES256 256 + +static void aes256_init(ops_crypt_t *crypt) + { + if (crypt->encrypt_key) + free(crypt->encrypt_key); + crypt->encrypt_key=malloc(sizeof(AES_KEY)); + if (AES_set_encrypt_key(crypt->key,KEYBITS_AES256,crypt->encrypt_key)) + fprintf(stderr,"aes256_init: Error setting encrypt_key\n"); + + if (crypt->decrypt_key) + free(crypt->decrypt_key); + crypt->decrypt_key=malloc(sizeof(AES_KEY)); + if (AES_set_decrypt_key(crypt->key,KEYBITS_AES256,crypt->decrypt_key)) + fprintf(stderr,"aes256_init: Error setting decrypt_key\n"); + } + +static const ops_crypt_t aes256= + { + OPS_SA_AES_256, + AES_BLOCK_SIZE, + KEYBITS_AES256/8, + std_set_iv, + std_set_key, + aes256_init, + std_resync, + aes_block_encrypt, + aes_block_decrypt, + aes_cfb_encrypt, + aes_cfb_decrypt, + std_finish, + TRAILER + }; + +// Triple DES + +static void tripledes_init(ops_crypt_t *crypt) + { + DES_key_schedule *keys; + int n; + + if (crypt->encrypt_key) + free(crypt->encrypt_key); + keys=crypt->encrypt_key=malloc(3*sizeof(DES_key_schedule)); + + for(n=0 ; n < 3 ; ++n) + DES_set_key((DES_cblock *)(crypt->key+n*8),&keys[n]); + } + +static void tripledes_block_encrypt(ops_crypt_t *crypt,void *out, + const void *in) + { + DES_key_schedule *keys=crypt->encrypt_key; + + DES_ecb3_encrypt((void *)in,out,&keys[0],&keys[1],&keys[2],DES_ENCRYPT); + } + +static void tripledes_block_decrypt(ops_crypt_t *crypt,void *out, + const void *in) + { + DES_key_schedule *keys=crypt->encrypt_key; + + DES_ecb3_encrypt((void *)in,out,&keys[0],&keys[1],&keys[2],DES_DECRYPT); + } + +static void tripledes_cfb_encrypt(ops_crypt_t *crypt ATTRIBUTE_UNUSED,void *out ATTRIBUTE_UNUSED,const void *in ATTRIBUTE_UNUSED, size_t count ATTRIBUTE_UNUSED) + { + DES_key_schedule *keys=crypt->encrypt_key; + DES_ede3_cfb64_encrypt(in,out,count, + &keys[0],&keys[1],&keys[2], (DES_cblock *)crypt->iv, (int *)&crypt->num, + DES_ENCRYPT); + } + +static void tripledes_cfb_decrypt(ops_crypt_t *crypt ATTRIBUTE_UNUSED,void *out ATTRIBUTE_UNUSED,const void *in ATTRIBUTE_UNUSED, size_t count ATTRIBUTE_UNUSED) + { + DES_key_schedule *keys=crypt->encrypt_key; + DES_ede3_cfb64_encrypt(in,out,count, + &keys[0],&keys[1],&keys[2], (DES_cblock *)crypt->iv, (int *)&crypt->num, + DES_DECRYPT); + } + +static const ops_crypt_t tripledes= + { + OPS_SA_TRIPLEDES, + 8, + 24, + std_set_iv, + std_set_key, + tripledes_init, + std_resync, + tripledes_block_encrypt, + tripledes_block_decrypt, + tripledes_cfb_encrypt, + tripledes_cfb_decrypt, + std_finish, + TRAILER + }; + +static const ops_crypt_t *get_proto(ops_symmetric_algorithm_t alg) + { + switch(alg) + { + case OPS_SA_CAST5: + return &cast5; + +#ifndef OPENSSL_NO_IDEA + case OPS_SA_IDEA: + return &idea; +#endif /* OPENSSL_NO_IDEA */ + + case OPS_SA_AES_128: + return &aes128; + + case OPS_SA_AES_256: + return &aes256; + + case OPS_SA_TRIPLEDES: + return &tripledes; + + default: + fprintf(stderr,"Unknown algorithm: %d (%s)\n",alg,ops_show_symmetric_algorithm(alg)); + // assert(0); + } + + return NULL; + } + +int ops_crypt_any(ops_crypt_t *crypt,ops_symmetric_algorithm_t alg) + { + const ops_crypt_t *ptr=get_proto(alg); + if (ptr) + { + *crypt=*ptr; + return 1; + } + else + { + memset(crypt,'\0',sizeof *crypt); + return 0; + } + } + +unsigned ops_block_size(ops_symmetric_algorithm_t alg) + { + const ops_crypt_t *p=get_proto(alg); + + if(!p) + return 0; + + return p->blocksize; + } + +unsigned ops_key_size(ops_symmetric_algorithm_t alg) + { + const ops_crypt_t *p=get_proto(alg); + + if(!p) + return 0; + + return p->keysize; + } + +void ops_encrypt_init(ops_crypt_t * encrypt) + { + // \todo should there be a separate ops_encrypt_init? + ops_decrypt_init(encrypt); + } + +void ops_decrypt_init(ops_crypt_t *decrypt) + { + decrypt->base_init(decrypt); + decrypt->block_encrypt(decrypt,decrypt->siv,decrypt->iv); + memcpy(decrypt->civ,decrypt->siv,decrypt->blocksize); + decrypt->num=0; + } + +size_t ops_decrypt_se +(ops_crypt_t *decrypt,void *out_,const void *in_, + size_t count) + { + unsigned char *out=out_; + const unsigned char *in=in_; + int saved=count; + + /* in order to support v3's weird resyncing we have to implement CFB mode + ourselves */ + while(count-- > 0) + { + unsigned char t; + + if(decrypt->num == decrypt->blocksize) + { + memcpy(decrypt->siv,decrypt->civ,decrypt->blocksize); + decrypt->block_decrypt(decrypt,decrypt->civ,decrypt->civ); + decrypt->num=0; + } + t=decrypt->civ[decrypt->num]; + *out++=t^(decrypt->civ[decrypt->num++]=*in++); + } + + return saved; + } + +size_t ops_encrypt_se(ops_crypt_t *encrypt,void *out_,const void *in_, + size_t count) + { + unsigned char *out=out_; + const unsigned char *in=in_; + int saved=count; + + /* in order to support v3's weird resyncing we have to implement CFB mode + ourselves */ + while(count-- > 0) + { + if(encrypt->num == encrypt->blocksize) + { + memcpy(encrypt->siv,encrypt->civ,encrypt->blocksize); + encrypt->block_encrypt(encrypt,encrypt->civ,encrypt->civ); + encrypt->num=0; + } + encrypt->civ[encrypt->num]=*out++=encrypt->civ[encrypt->num]^*in++; + ++encrypt->num; + } + + return saved; + } + +/** +\ingroup HighLevel_Supported +\brief Is this Symmetric Algorithm supported? +\param alg Symmetric Algorithm to check +\return ops_true if supported; else ops_false +*/ +ops_boolean_t ops_is_sa_supported(ops_symmetric_algorithm_t alg) + { + switch (alg) + { + case OPS_SA_AES_128: + case OPS_SA_AES_256: + case OPS_SA_CAST5: + case OPS_SA_TRIPLEDES: +#ifndef OPENSSL_NO_IDEA + case OPS_SA_IDEA: +#endif + return ops_true; + break; + + default: + fprintf(stderr,"\nWarning: %s not supported\n", + ops_show_symmetric_algorithm(alg)); + return ops_false; + } + } + +size_t ops_encrypt_se_ip(ops_crypt_t *crypt,void *out_,const void *in_, + size_t count) + { + if (!ops_is_sa_supported(crypt->algorithm)) + return -1; + + crypt->cfb_encrypt(crypt, out_, in_, count); + + // \todo test this number was encrypted + return count; + } + +size_t ops_decrypt_se_ip(ops_crypt_t *crypt,void *out_,const void *in_, + size_t count) + { + if (!ops_is_sa_supported(crypt->algorithm)) + return -1; + + crypt->cfb_decrypt(crypt, out_, in_, count); + + // \todo check this number was in fact decrypted + return count; + } + +// EOF diff --git a/openpgpsdk/src/util.c b/openpgpsdk/src/util.c new file mode 100644 index 000000000..8e4fd3846 --- /dev/null +++ b/openpgpsdk/src/util.c @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifndef WIN32 +#include +#endif + +#include + +#include + +/** + * Searches the given map for the given type. + * Returns a human-readable descriptive string if found, + * returns NULL if not found + * + * It is the responsibility of the calling function to handle the + * error case sensibly (i.e. don't just print out the return string. + * + */ +static char *str_from_map_or_null(int type, ops_map_t *map) + { + ops_map_t *row; + + for ( row=map; row->string != NULL; row++ ) + if (row->type == type) + return row->string; + return NULL; + } + +/** + * \ingroup Core_Print + * + * Searches the given map for the given type. + * Returns a readable string if found, "Unknown" if not. + */ + +char *ops_str_from_map(int type, ops_map_t *map) + { + char *str; + str=str_from_map_or_null(type,map); + if (str) + return(str); + else + return("Unknown"); + } + +void hexdump(const unsigned char *src,size_t length) + { + while(length--) + printf("%02X",*src++); + } + +/** + * \ingroup HighLevel_Functions + * \brief Initialises OpenPGP::SDK. To be called before any other OPS function. + * + * Initialises OpenPGP::SDK and the underlying openssl library. + */ + +void ops_init(void) + { + ops_crypto_init(); + } + +/** + * \ingroup HighLevel_Functions + * \brief Closes down OpenPGP::SDK. + * + * Close down OpenPGP:SDK, release any resources under the control of + * the library. No OpenPGP:SDK function other than ops_init() should + * be called after this function. + */ + +void ops_finish(void) + { + ops_crypto_finish(); + } + +typedef struct + { + const unsigned char *buffer; + size_t length; + size_t offset; + } reader_mem_arg_t; + +static int mem_reader(void *dest,size_t length,ops_error_t **errors, + ops_reader_info_t *rinfo,ops_parse_cb_info_t *cbinfo) + { + reader_mem_arg_t *arg=ops_reader_get_arg(rinfo); + unsigned n; + + OPS_USED(cbinfo); + OPS_USED(errors); + + if(arg->offset+length > arg->length) + n=arg->length-arg->offset; + else + n=length; + + if(n == 0) + return 0; + + memcpy(dest,arg->buffer+arg->offset,n); + arg->offset+=n; + + return n; + } + +static void mem_destroyer(ops_reader_info_t *rinfo) + { free(ops_reader_get_arg(rinfo)); } + +// Note that its the caller's responsibility to ensure buffer continues to +// exist +void ops_reader_set_memory(ops_parse_info_t *pinfo,const void *buffer, + size_t length) + { + reader_mem_arg_t *arg=malloc(sizeof *arg); + + arg->buffer=buffer; + arg->length=length; + arg->offset=0; + ops_reader_set(pinfo,mem_reader,mem_destroyer,arg); + } + +/** + \ingroup HighLevel_Misc + \brief mallocs and zeros memory + \param n Number of bytes to be alloc-ed. + \return Pointer to new memory. + \note Should be freed after use with free(). +*/ +void *ops_mallocz(size_t n) + { + void *m=malloc(n); + + memset(m,'\0',n); + + return m; + } + +typedef struct + { + unsigned short sum; + } sum16_arg_t; + +static int sum16_reader(void *dest_,size_t length,ops_error_t **errors, + ops_reader_info_t *rinfo,ops_parse_cb_info_t *cbinfo) + { + const unsigned char *dest=dest_; + sum16_arg_t *arg=ops_reader_get_arg(rinfo); + int r=ops_stacked_read(dest_,length,errors,rinfo,cbinfo); + int n; + + if(r < 0) + return r; + + for(n=0 ; n < r ; ++n) + arg->sum=(arg->sum+dest[n])&0xffff; + + return r; + } + +static void sum16_destroyer(ops_reader_info_t *rinfo) + { free(ops_reader_get_arg(rinfo)); } + +/** + \ingroup Internal_Readers_Sum16 + \param pinfo Parse settings +*/ + +void ops_reader_push_sum16(ops_parse_info_t *pinfo) + { + sum16_arg_t *arg=ops_mallocz(sizeof *arg); + + ops_reader_push(pinfo,sum16_reader,sum16_destroyer,arg); + } + +/** + \ingroup Internal_Readers_Sum16 + \param pinfo Parse settings + \return sum +*/ +unsigned short ops_reader_pop_sum16(ops_parse_info_t *pinfo) + { + sum16_arg_t *arg=ops_reader_get_arg(ops_parse_get_rinfo(pinfo)); + unsigned short sum=arg->sum; + + ops_reader_pop(pinfo); + free(arg); + + return sum; + } diff --git a/openpgpsdk/src/validate.c b/openpgpsdk/src/validate.c new file mode 100644 index 000000000..4070b9181 --- /dev/null +++ b/openpgpsdk/src/validate.c @@ -0,0 +1,746 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "keyring_local.h" +#include "parse_local.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static int debug=0; + +static ops_boolean_t check_binary_signature(const unsigned len, + const unsigned char *data, + const ops_signature_t *sig, + const ops_public_key_t *signer __attribute__((unused))) + { + // Does the signed hash match the given hash? + + int n=0; + ops_hash_t hash; + unsigned char hashout[OPS_MAX_HASH_SIZE]; + unsigned char trailer[6]; + unsigned int hashedlen; + + //common_init_signature(&hash,sig); + ops_hash_any(&hash,sig->info.hash_algorithm); + hash.init(&hash); + hash.add(&hash,data,len); + switch (sig->info.version) + { + case OPS_V3: + trailer[0]=sig->info.type; + trailer[1]=sig->info.creation_time >> 24; + trailer[2]=sig->info.creation_time >> 16; + trailer[3]=sig->info.creation_time >> 8; + trailer[4]=sig->info.creation_time; + hash.add(&hash,&trailer[0],5); + break; + + case OPS_V4: + hash.add(&hash,sig->info.v4_hashed_data,sig->info.v4_hashed_data_length); + + trailer[0]=0x04; // version + trailer[1]=0xFF; + hashedlen=sig->info.v4_hashed_data_length; + trailer[2]=hashedlen >> 24; + trailer[3]=hashedlen >> 16; + trailer[4]=hashedlen >> 8; + trailer[5]=hashedlen; + hash.add(&hash,&trailer[0],6); + + break; + + default: + fprintf(stderr,"Invalid signature version %d\n", sig->info.version); + return ops_false; + } + + n=hash.finish(&hash,hashout); + + // return ops_false; + return ops_check_signature(hashout,n,sig,signer); + } + +static int keydata_reader(void *dest,size_t length,ops_error_t **errors, + ops_reader_info_t *rinfo, + ops_parse_cb_info_t *cbinfo) + { + validate_reader_arg_t *arg=ops_reader_get_arg(rinfo); + + OPS_USED(errors); + OPS_USED(cbinfo); + if(arg->offset == arg->key->packets[arg->packet].length) + { + ++arg->packet; + arg->offset=0; + } + + if(arg->packet == arg->key->npackets) + return 0; + + // we should never be asked to cross a packet boundary in a single read + assert(arg->key->packets[arg->packet].length >= arg->offset+length); + + memcpy(dest,&arg->key->packets[arg->packet].raw[arg->offset],length); + arg->offset+=length; + + return length; + } + +static void free_signature_info(ops_signature_info_t *sig) + { + free (sig->v4_hashed_data); + free (sig); + } + +static void copy_signature_info(ops_signature_info_t* dst, const ops_signature_info_t* src) + { + memcpy(dst,src,sizeof *src); + dst->v4_hashed_data=ops_mallocz(src->v4_hashed_data_length); + memcpy(dst->v4_hashed_data,src->v4_hashed_data,src->v4_hashed_data_length); + } + +static void add_sig_to_valid_list(ops_validate_result_t * result, const ops_signature_info_t* sig) + { + size_t newsize; + size_t start; + + // increment count + ++result->valid_count; + + // increase size of array + newsize=(sizeof *sig) * result->valid_count; + if (!result->valid_sigs) + result->valid_sigs=malloc(newsize); + else + result->valid_sigs=realloc(result->valid_sigs, newsize); + + // copy key ptr to array + start=(sizeof *sig) * (result->valid_count-1); + copy_signature_info(result->valid_sigs+start,sig); + } + +static void add_sig_to_invalid_list(ops_validate_result_t * result, const ops_signature_info_t *sig) + { + size_t newsize; + size_t start; + + // increment count + ++result->invalid_count; + + // increase size of array + newsize=(sizeof *sig) * result->invalid_count; + if (!result->invalid_sigs) + result->invalid_sigs=malloc(newsize); + else + result->invalid_sigs=realloc(result->invalid_sigs, newsize); + + // copy key ptr to array + start=(sizeof *sig) * (result->invalid_count-1); + copy_signature_info(result->invalid_sigs+start, sig); + } + +static void add_sig_to_unknown_list(ops_validate_result_t * result, const ops_signature_info_t *sig) + { + size_t newsize; + size_t start; + + // increment count + ++result->unknown_signer_count; + + // increase size of array + newsize=(sizeof *sig) * result->unknown_signer_count; + if (!result->unknown_sigs) + result->unknown_sigs=malloc(newsize); + else + result->unknown_sigs=realloc(result->unknown_sigs, newsize); + + // copy key id to array + start=OPS_KEY_ID_SIZE * (result->unknown_signer_count-1); + copy_signature_info(result->unknown_sigs+start, sig); + } + +ops_parse_cb_return_t +ops_validate_key_cb(const ops_parser_content_t *content_,ops_parse_cb_info_t *cbinfo) + { + const ops_parser_content_union_t *content=&content_->content; + validate_key_cb_arg_t *arg=ops_parse_cb_get_arg(cbinfo); + ops_error_t **errors=ops_parse_cb_get_errors(cbinfo); + const ops_keydata_t *signer; + ops_boolean_t valid=ops_false; + + if (debug) + printf("%s\n",ops_show_packet_tag(content_->tag)); + + switch(content_->tag) + { + case OPS_PTAG_CT_PUBLIC_KEY: + assert(arg->pkey.version == 0); + arg->pkey=content->public_key; + return OPS_KEEP_MEMORY; + + case OPS_PTAG_CT_PUBLIC_SUBKEY: + if(arg->subkey.version) + ops_public_key_free(&arg->subkey); + arg->subkey=content->public_key; + return OPS_KEEP_MEMORY; + + case OPS_PTAG_CT_SECRET_KEY: + arg->skey=content->secret_key; + arg->pkey=arg->skey.public_key; + return OPS_KEEP_MEMORY; + + case OPS_PTAG_CT_USER_ID: + if(arg->user_id.user_id) + ops_user_id_free(&arg->user_id); + arg->user_id=content->user_id; + arg->last_seen=ID; + return OPS_KEEP_MEMORY; + + case OPS_PTAG_CT_USER_ATTRIBUTE: + assert(content->user_attribute.data.len); + printf("user attribute, length=%d\n",(int)content->user_attribute.data.len); + if(arg->user_attribute.data.len) + ops_user_attribute_free(&arg->user_attribute); + arg->user_attribute=content->user_attribute; + arg->last_seen=ATTRIBUTE; + return OPS_KEEP_MEMORY; + + case OPS_PTAG_CT_SIGNATURE: // V3 sigs + case OPS_PTAG_CT_SIGNATURE_FOOTER: // V4 sigs + /* + printf(" type=%02x signer_id=",content->signature.type); + hexdump(content->signature.signer_id, + sizeof content->signature.signer_id); + */ + + signer=ops_keyring_find_key_by_id(arg->keyring, + content->signature.info.signer_id); + if(!signer) + { + add_sig_to_unknown_list(arg->result, &content->signature.info); + break; + } + + switch(content->signature.info.type) + { + case OPS_CERT_GENERIC: + case OPS_CERT_PERSONA: + case OPS_CERT_CASUAL: + case OPS_CERT_POSITIVE: + case OPS_SIG_REV_CERT: + if(arg->last_seen == ID) + valid=ops_check_user_id_certification_signature(&arg->pkey, + &arg->user_id, + &content->signature, + ops_get_public_key_from_data(signer), + arg->rarg->key->packets[arg->rarg->packet].raw); + else + valid=ops_check_user_attribute_certification_signature(&arg->pkey, + &arg->user_attribute, + &content->signature, + ops_get_public_key_from_data(signer), + arg->rarg->key->packets[arg->rarg->packet].raw); + + break; + + case OPS_SIG_SUBKEY: + // XXX: we should also check that the signer is the key we are validating, I think. + valid=ops_check_subkey_signature(&arg->pkey,&arg->subkey, + &content->signature, + ops_get_public_key_from_data(signer), + arg->rarg->key->packets[arg->rarg->packet].raw); + break; + + case OPS_SIG_DIRECT: + valid=ops_check_direct_signature(&arg->pkey,&content->signature, + ops_get_public_key_from_data(signer), + arg->rarg->key->packets[arg->rarg->packet].raw); + break; + + case OPS_SIG_STANDALONE: + case OPS_SIG_PRIMARY: + case OPS_SIG_REV_KEY: + case OPS_SIG_REV_SUBKEY: + case OPS_SIG_TIMESTAMP: + case OPS_SIG_3RD_PARTY: + OPS_ERROR_1(errors, OPS_E_UNIMPLEMENTED, + "Verification of signature type 0x%02x not yet implemented\n", content->signature.info.type); + break; + + default: + OPS_ERROR_1(errors, OPS_E_UNIMPLEMENTED, + "Unexpected signature type 0x%02x\n", content->signature.info.type); + } + + if(valid) + { + // printf(" validated\n"); + //++arg->result->valid_count; + add_sig_to_valid_list(arg->result, &content->signature.info); + } + else + { + OPS_ERROR(errors,OPS_E_V_BAD_SIGNATURE,"Bad Signature"); + // printf(" BAD SIGNATURE\n"); + // ++arg->result->invalid_count; + add_sig_to_invalid_list(arg->result, &content->signature.info); + } + break; + + // ignore these + case OPS_PARSER_PTAG: + case OPS_PTAG_CT_SIGNATURE_HEADER: + case OPS_PARSER_PACKET_END: + break; + + case OPS_PARSER_CMD_GET_SK_PASSPHRASE: + if (arg->cb_get_passphrase) + { + return arg->cb_get_passphrase(content_,cbinfo); + } + break; + + default: + fprintf(stderr,"unexpected tag=0x%x\n",content_->tag); + assert(0); + break; + } + return OPS_RELEASE_MEMORY; + } + +ops_parse_cb_return_t +validate_data_cb(const ops_parser_content_t *content_,ops_parse_cb_info_t *cbinfo) + { + const ops_parser_content_union_t *content=&content_->content; + validate_data_cb_arg_t *arg=ops_parse_cb_get_arg(cbinfo); + ops_error_t **errors=ops_parse_cb_get_errors(cbinfo); + const ops_keydata_t *signer; + ops_boolean_t valid=ops_false; + ops_memory_t* mem=NULL; + + if (debug) + printf("%s\n",ops_show_packet_tag(content_->tag)); + + switch(content_->tag) + { + case OPS_PTAG_CT_SIGNED_CLEARTEXT_HEADER: + // ignore - this gives us the "Armor Header" line "Hash: SHA1" or similar + break; + + case OPS_PTAG_CT_LITERAL_DATA_HEADER: + // ignore + break; + + case OPS_PTAG_CT_LITERAL_DATA_BODY: + arg->data.literal_data_body=content->literal_data_body; + arg->use=LITERAL_DATA; + return OPS_KEEP_MEMORY; + break; + + case OPS_PTAG_CT_SIGNED_CLEARTEXT_BODY: + arg->data.signed_cleartext_body=content->signed_cleartext_body; + arg->use=SIGNED_CLEARTEXT; + return OPS_KEEP_MEMORY; + break; + + case OPS_PTAG_CT_SIGNED_CLEARTEXT_TRAILER: + // this gives us an ops_hash_t struct + break; + + case OPS_PTAG_CT_SIGNATURE: // V3 sigs + case OPS_PTAG_CT_SIGNATURE_FOOTER: // V4 sigs + + if (debug) + { + printf("\n*** hashed data:\n"); + unsigned int zzz=0; + for (zzz=0; zzzsignature.info.v4_hashed_data_length; zzz++) + printf("0x%02x ", content->signature.info.v4_hashed_data[zzz]); + printf("\n"); + printf(" type=%02x signer_id=",content->signature.info.type); + hexdump(content->signature.info.signer_id, + sizeof content->signature.info.signer_id); + } + + signer=ops_keyring_find_key_by_id(arg->keyring, + content->signature.info.signer_id); + if(!signer) + { + OPS_ERROR(errors,OPS_E_V_UNKNOWN_SIGNER,"Unknown Signer"); + add_sig_to_unknown_list(arg->result, &content->signature.info); + break; + } + + mem=ops_memory_new(); + ops_memory_init(mem,128); + + switch(content->signature.info.type) + { + case OPS_SIG_BINARY: + case OPS_SIG_TEXT: + switch(arg->use) + { + case LITERAL_DATA: + ops_memory_add(mem, + arg->data.literal_data_body.data, + arg->data.literal_data_body.length); + break; + + case SIGNED_CLEARTEXT: + ops_memory_add(mem, + arg->data.signed_cleartext_body.data, + arg->data.signed_cleartext_body.length); + break; + + default: + OPS_ERROR_1(errors,OPS_E_UNIMPLEMENTED,"Unimplemented Sig Use %d", arg->use); + printf(" Unimplemented Sig Use %d\n", arg->use); + break; + } + + valid=check_binary_signature(ops_memory_get_length(mem), + ops_memory_get_data(mem), + &content->signature, + ops_get_public_key_from_data(signer)); + break; + + default: + OPS_ERROR_1(errors, OPS_E_UNIMPLEMENTED, + "Verification of signature type 0x%02x not yet implemented\n", content->signature.info.type); + break; + + } + ops_memory_free(mem); + + if(valid) + { + add_sig_to_valid_list(arg->result, &content->signature.info); + } + else + { + OPS_ERROR(errors,OPS_E_V_BAD_SIGNATURE,"Bad Signature"); + add_sig_to_invalid_list(arg->result, &content->signature.info); + } + break; + + // ignore these + case OPS_PARSER_PTAG: + case OPS_PTAG_CT_SIGNATURE_HEADER: + case OPS_PTAG_CT_ARMOUR_HEADER: + case OPS_PTAG_CT_ARMOUR_TRAILER: + case OPS_PTAG_CT_ONE_PASS_SIGNATURE: + case OPS_PARSER_PACKET_END: + break; + + default: + fprintf(stderr,"unexpected tag=0x%x\n",content_->tag); + assert(0); + break; + } + return OPS_RELEASE_MEMORY; + } + +static void keydata_destroyer(ops_reader_info_t *rinfo) + { free(ops_reader_get_arg(rinfo)); } + +void ops_keydata_reader_set(ops_parse_info_t *pinfo,const ops_keydata_t *key) + { + validate_reader_arg_t *arg=malloc(sizeof *arg); + + memset(arg,'\0',sizeof *arg); + + arg->key=key; + arg->packet=0; + arg->offset=0; + + ops_reader_set(pinfo,keydata_reader,keydata_destroyer,arg); + } + +/** + * \ingroup HighLevel_Verify + * \brief Indicicates whether any errors were found + * \param result Validation result to check + * \return ops_false if any invalid signatures or unknown signers or no valid signatures; else ops_true + */ +ops_boolean_t validate_result_status(ops_validate_result_t* result) + { + if (result->invalid_count || result->unknown_signer_count || !result->valid_count) + return ops_false; + else + return ops_true; + } + +/** + * \ingroup HighLevel_Verify + * \brief Validate all signatures on a single key against the given keyring + * \param result Where to put the result + * \param key Key to validate + * \param keyring Keyring to use for validation + * \param cb_get_passphrase Callback to use to get passphrase + * \return ops_true if all signatures OK; else ops_false + * \note It is the caller's responsiblity to free result after use. + * \sa ops_validate_result_free() + + Example Code: +\code +void example(const ops_keydata_t* key, const ops_keyring_t *keyring) +{ + ops_validate_result_t *result=NULL; + if (ops_validate_key_signatures(result, key, keyring, callback_cmd_get_passphrase_from_cmdline)==ops_true) + printf("OK"); + else + printf("ERR"); + printf("valid=%d, invalid=%d, unknown=%d\n", + result->valid_count, + result->invalid_count, + result->unknown_signer_count); + ops_validate_result_free(result); +} + +\endcode + */ +ops_boolean_t ops_validate_key_signatures(ops_validate_result_t *result,const ops_keydata_t *key, + const ops_keyring_t *keyring, + ops_parse_cb_return_t cb_get_passphrase (const ops_parser_content_t *, ops_parse_cb_info_t *) + ) + { + ops_parse_info_t *pinfo; + validate_key_cb_arg_t carg; + + memset(&carg,'\0',sizeof carg); + carg.result=result; + carg.cb_get_passphrase=cb_get_passphrase; + + pinfo=ops_parse_info_new(); + // ops_parse_options(&opt,OPS_PTAG_CT_SIGNATURE,OPS_PARSE_PARSED); + + carg.keyring=keyring; + + ops_parse_cb_set(pinfo,ops_validate_key_cb,&carg); + pinfo->rinfo.accumulate=ops_true; + ops_keydata_reader_set(pinfo,key); + + // Note: Coverity incorrectly reports an error that carg.rarg + // is never used. + carg.rarg=ops_reader_get_arg_from_pinfo(pinfo); + + ops_parse(pinfo); + + ops_public_key_free(&carg.pkey); + if(carg.subkey.version) + ops_public_key_free(&carg.subkey); + ops_user_id_free(&carg.user_id); + ops_user_attribute_free(&carg.user_attribute); + + ops_parse_info_delete(pinfo); + + if (result->invalid_count || result->unknown_signer_count || !result->valid_count) + return ops_false; + else + return ops_true; + } + +/** + \ingroup HighLevel_Verify + \param result Where to put the result + \param ring Keyring to use + \param cb_get_passphrase Callback to use to get passphrase + \note It is the caller's responsibility to free result after use. + \sa ops_validate_result_free() +*/ +ops_boolean_t ops_validate_all_signatures(ops_validate_result_t *result, + const ops_keyring_t *ring, + ops_parse_cb_return_t cb_get_passphrase (const ops_parser_content_t *, ops_parse_cb_info_t *) + ) + { + int n; + + memset(result,'\0',sizeof *result); + for(n=0 ; n < ring->nkeys ; ++n) + ops_validate_key_signatures(result,&ring->keys[n],ring, cb_get_passphrase); + return validate_result_status(result); + } + +/** + \ingroup HighLevel_Verify + \brief Frees validation result and associated memory + \param result Struct to be freed + \note Must be called after validation functions +*/ +void ops_validate_result_free(ops_validate_result_t *result) + { + if (!result) + return; + + if (result->valid_sigs) + free_signature_info(result->valid_sigs); + if (result->invalid_sigs) + free_signature_info(result->invalid_sigs); + if (result->unknown_sigs) + free_signature_info(result->unknown_sigs); + + free(result); + result=NULL; + } + +/** + \ingroup HighLevel_Verify + \brief Verifies the signatures in a signed file + \param result Where to put the result + \param filename Name of file to be validated + \param armoured Treat file as armoured, if set + \param keyring Keyring to use + \return ops_true if signatures validate successfully; ops_false if signatures fail or there are no signatures + \note After verification, result holds the details of all keys which + have passed, failed and not been recognised. + \note It is the caller's responsiblity to call ops_validate_result_free(result) after use. + +Example code: +\code +void example(const char* filename, const int armoured, const ops_keyring_t* keyring) +{ + ops_validate_result_t* result=ops_mallocz(sizeof *result); + + if (ops_validate_file(result, filename, armoured, keyring)==ops_true) + { + printf("OK"); + // look at result for details of keys with good signatures + } + else + { + printf("ERR"); + // look at result for details of failed signatures or unknown signers + } + + ops_validate_result_free(result); +} +\endcode +*/ +ops_boolean_t ops_validate_file(ops_validate_result_t *result, const char* filename, const int armoured, const ops_keyring_t* keyring) + { + ops_parse_info_t *pinfo=NULL; + validate_data_cb_arg_t validate_arg; + + int fd=0; + + // + fd=ops_setup_file_read(&pinfo, filename, &validate_arg, validate_data_cb, ops_true); + if (fd < 0) + return ops_false; + + // Set verification reader and handling options + + memset(&validate_arg,'\0',sizeof validate_arg); + validate_arg.result=result; + validate_arg.keyring=keyring; + // Note: Coverity incorrectly reports an error that carg.rarg + // is never used. + validate_arg.rarg=ops_reader_get_arg_from_pinfo(pinfo); + + if (armoured) + ops_reader_push_dearmour(pinfo); + + // Do the verification + + ops_parse(pinfo); + + if (debug) + { + printf("valid=%d, invalid=%d, unknown=%d\n", + result->valid_count, + result->invalid_count, + result->unknown_signer_count); + } + + // Tidy up + if (armoured) + ops_reader_pop_dearmour(pinfo); + ops_teardown_file_read(pinfo, fd); + + return validate_result_status(result); + } + +/** + \ingroup HighLevel_Verify + \brief Verifies the signatures in a ops_memory_t struct + \param result Where to put the result + \param mem Memory to be validated + \param armoured Treat data as armoured, if set + \param keyring Keyring to use + \return ops_true if signature validates successfully; ops_false if not + \note After verification, result holds the details of all keys which + have passed, failed and not been recognised. + \note It is the caller's responsiblity to call ops_validate_result_free(result) after use. +*/ + +ops_boolean_t ops_validate_mem(ops_validate_result_t *result, ops_memory_t* mem, const int armoured, const ops_keyring_t* keyring) + { + ops_parse_info_t *pinfo=NULL; + validate_data_cb_arg_t validate_arg; + + // + ops_setup_memory_read(&pinfo, mem, &validate_arg, validate_data_cb, ops_true); + + // Set verification reader and handling options + + memset(&validate_arg,'\0',sizeof validate_arg); + validate_arg.result=result; + validate_arg.keyring=keyring; + // Note: Coverity incorrectly reports an error that carg.rarg + // is never used. + validate_arg.rarg=ops_reader_get_arg_from_pinfo(pinfo); + + if (armoured) + ops_reader_push_dearmour(pinfo); + + // Do the verification + + ops_parse(pinfo); + + if (debug) + { + printf("valid=%d, invalid=%d, unknown=%d\n", + result->valid_count, + result->invalid_count, + result->unknown_signer_count); + } + + // Tidy up + if (armoured) + ops_reader_pop_dearmour(pinfo); + ops_teardown_memory_read(pinfo, mem); + + return validate_result_status(result); + } + +// eof diff --git a/openpgpsdk/src/writer.c b/openpgpsdk/src/writer.c new file mode 100644 index 000000000..503f48cad --- /dev/null +++ b/openpgpsdk/src/writer.c @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + * This file contains the base functions used by the writers. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include "keyring_local.h" +#include +#include +#include +#include +#include +#include +#ifndef WIN32 +#include +#endif + +#include + +//static int debug=0; + +/* + * return true if OK, otherwise false + */ +static ops_boolean_t base_write(const void *src,unsigned length, + ops_create_info_t *info) + { + return info->winfo.writer(src,length,&info->errors,&info->winfo); + } + +/** + * \ingroup Core_WritePackets + * + * \param src + * \param length + * \param info + * \return 1 if OK, otherwise 0 + */ + +ops_boolean_t ops_write(const void *src,unsigned length, + ops_create_info_t *info) + { + return base_write(src,length,info); + } + +/** + * \ingroup Core_WritePackets + * \param n + * \param length + * \param info + * \return ops_true if OK, otherwise ops_false + */ + +ops_boolean_t ops_write_scalar(unsigned n,unsigned length, + ops_create_info_t *info) + { + while(length-- > 0) + { + unsigned char c[1]; + + c[0]=n >> (length*8); + if(!base_write(c,1,info)) + return ops_false; + } + return ops_true; + } + +/** + * \ingroup Core_WritePackets + * \param bn + * \param info + * \return 1 if OK, otherwise 0 + */ + +ops_boolean_t ops_write_mpi(const BIGNUM *bn,ops_create_info_t *info) + { + unsigned char buf[8192]; + int bits=BN_num_bits(bn); + + assert(bits <= 65535); + BN_bn2bin(bn,buf); + return ops_write_scalar(bits,2,info) + && ops_write(buf,(bits+7)/8,info); + } + +/** + * \ingroup Core_WritePackets + * \param tag + * \param info + * \return 1 if OK, otherwise 0 + */ + +ops_boolean_t ops_write_ptag(ops_content_tag_t tag,ops_create_info_t *info) + { + unsigned char c[1]; + + c[0]=tag|OPS_PTAG_ALWAYS_SET|OPS_PTAG_NEW_FORMAT; + + return base_write(c,1,info); + } + +/** + * \ingroup Core_WritePackets + * \param length + * \param info + * \return 1 if OK, otherwise 0 + */ + +ops_boolean_t ops_write_length(unsigned length,ops_create_info_t *info) + { + unsigned char c[2]; + + if(length < 192) + { + c[0]=length; + return base_write(c,1,info); + } + else if(length < 8384) + { + c[0]=((length-192) >> 8)+192; + c[1]=(length-192)%256; + return base_write(c,2,info); + } + return ops_write_scalar(0xff,1,info) && ops_write_scalar(length,4,info); + } + +/* Note that we finalise from the top down, so we don't use writers below + * that have already been finalised + */ +ops_boolean_t writer_info_finalise(ops_error_t **errors, + ops_writer_info_t *winfo) + { + ops_boolean_t ret=ops_true; + + if(winfo->finaliser) + { + ret=winfo->finaliser(errors,winfo); + winfo->finaliser=NULL; + } + if(winfo->next && !writer_info_finalise(errors,winfo->next)) + { + winfo->finaliser=NULL; + return ops_false; + } + return ret; + } + +void writer_info_delete(ops_writer_info_t *winfo) + { + // we should have finalised before deleting + assert(!winfo->finaliser); + if(winfo->next) + { + writer_info_delete(winfo->next); + free(winfo->next); + winfo->next=NULL; + } + if(winfo->destroyer) + { + winfo->destroyer(winfo); + winfo->destroyer=NULL; + } + winfo->writer=NULL; + } + +/** + * \ingroup Core_Writers + * + * Set a writer in info. There should not be another writer set. + * + * \param info The info structure + * \param writer + * \param finaliser + * \param destroyer + * \param arg The argument for the writer and destroyer + */ +void ops_writer_set(ops_create_info_t *info, + ops_writer_t *writer, + ops_writer_finaliser_t *finaliser, + ops_writer_destroyer_t *destroyer, + void *arg) + { + assert(!info->winfo.writer); + info->winfo.writer=writer; + info->winfo.finaliser=finaliser; + info->winfo.destroyer=destroyer; + info->winfo.arg=arg; + } + +/** + * \ingroup Core_Writers + * + * Push a writer in info. There must already be another writer set. + * + * \param info The info structure + * \param writer + * \param finaliser + * \param destroyer + * \param arg The argument for the writer and destroyer + */ +void ops_writer_push(ops_create_info_t *info, + ops_writer_t *writer, + ops_writer_finaliser_t *finaliser, + ops_writer_destroyer_t *destroyer, + void *arg) + { + ops_writer_info_t *copy=ops_mallocz(sizeof *copy); + + assert(info->winfo.writer); + *copy=info->winfo; + info->winfo.next=copy; + + info->winfo.writer=writer; + info->winfo.finaliser=finaliser; + info->winfo.destroyer=destroyer; + info->winfo.arg=arg; + } + +void ops_writer_pop(ops_create_info_t *info) + { + ops_writer_info_t *next; + + // Make sure the finaliser has been called. + assert(!info->winfo.finaliser); + // Make sure this is a stacked writer + assert(info->winfo.next); + if(info->winfo.destroyer) + info->winfo.destroyer(&info->winfo); + + next=info->winfo.next; + info->winfo=*next; + + free(next); + } + +/** + * \ingroup Core_Writers + * + * Close the writer currently set in info. + * + * \param info The info structure + */ +ops_boolean_t ops_writer_close(ops_create_info_t *info) + { + ops_boolean_t ret=writer_info_finalise(&info->errors,&info->winfo); + + writer_info_delete(&info->winfo); + + return ret; + } + +/** + * \ingroup Core_Writers + * + * Get the arg supplied to ops_create_info_set_writer(). + * + * \param winfo The writer_info structure + * \return The arg + */ +void *ops_writer_get_arg(ops_writer_info_t *winfo) + { return winfo->arg; } + +/** + * \ingroup Core_Writers + * + * Write to the next writer down in the stack. + * + * \param src The data to write. + * \param length The length of src. + * \param errors A place to store errors. + * \param winfo The writer_info structure. + * \return Success - if ops_false, then errors should contain the error. + */ +ops_boolean_t ops_stacked_write(const void *src,unsigned length, + ops_error_t **errors,ops_writer_info_t *winfo) + { + return winfo->next->writer(src,length,errors,winfo->next); + } + +/** + * \ingroup Core_Writers + * + * Free the arg. Many writers just have a malloc()ed lump of storage, this + * function releases it. + * + * \param winfo the info structure. + */ +void ops_writer_generic_destroyer(ops_writer_info_t *winfo) + { free(ops_writer_get_arg(winfo)); } + +/** + * \ingroup Core_Writers + * + * A writer that just writes to the next one down. Useful for when you + * want to insert just a finaliser into the stack. + */ +ops_boolean_t ops_writer_passthrough(const unsigned char *src, + unsigned length, + ops_error_t **errors, + ops_writer_info_t *winfo) + { return ops_stacked_write(src,length,errors,winfo); } + + +// EOF diff --git a/openpgpsdk/src/writer_armour.c b/openpgpsdk/src/writer_armour.c new file mode 100644 index 000000000..5b4417027 --- /dev/null +++ b/openpgpsdk/src/writer_armour.c @@ -0,0 +1,488 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + */ + +#include +#include + +#include +#include +#include +#include + +#include + +static int debug=0; + +/** + * \struct dash_escaped_arg_t + */ +typedef struct + { + ops_boolean_t seen_nl:1; + ops_boolean_t seen_cr:1; + ops_create_signature_t *sig; + ops_memory_t *trailing; + } dash_escaped_arg_t; + +static ops_boolean_t dash_escaped_writer(const unsigned char *src, + unsigned length, + ops_error_t **errors, + ops_writer_info_t *winfo) + { + dash_escaped_arg_t *arg=ops_writer_get_arg(winfo); + unsigned n; + + if (debug) + { + unsigned int i=0; + fprintf(stderr,"dash_escaped_writer writing %d:\n", length); + for (i=0; iseen_nl) + { + if(src[n] == '-' && !ops_stacked_write("- ",2,errors,winfo)) + return ops_false; + arg->seen_nl=ops_false; + } + + arg->seen_nl=src[n] == '\n'; + + if(arg->seen_nl && !arg->seen_cr) + { + if(!ops_stacked_write("\r",1,errors,winfo)) + return ops_false; + ops_signature_add_data(arg->sig,"\r",1); + } + + arg->seen_cr=src[n] == '\r'; + + if(!ops_stacked_write(&src[n],1,errors,winfo)) + return ops_false; + + /* trailing whitespace isn't included in the signature */ + if(src[n] == ' ' || src[n] == '\t') + ops_memory_add(arg->trailing,&src[n],1); + else + { + if((l=ops_memory_get_length(arg->trailing))) + { + if(!arg->seen_nl && !arg->seen_cr) + ops_signature_add_data(arg->sig, + ops_memory_get_data(arg->trailing), + l); + ops_memory_clear(arg->trailing); + } + ops_signature_add_data(arg->sig,&src[n],1); + } + } + + return ops_true; + } + +/** + * \param winfo + */ +static void dash_escaped_destroyer(ops_writer_info_t *winfo) + { + dash_escaped_arg_t *arg=ops_writer_get_arg(winfo); + + ops_memory_free(arg->trailing); + free(arg); + } + +/** + * \ingroup Core_WritersNext + * \brief Push Clearsigned Writer onto stack + * \param info + * \param sig + */ +ops_boolean_t ops_writer_push_clearsigned(ops_create_info_t *info, + ops_create_signature_t *sig) + { + static char header[]="-----BEGIN PGP SIGNED MESSAGE-----\r\nHash: "; + const char *hash=ops_text_from_hash(ops_signature_get_hash(sig)); + dash_escaped_arg_t *arg=ops_mallocz(sizeof *arg); + + ops_boolean_t rtn; + + rtn= ( ops_write(header,sizeof header-1,info) + && ops_write(hash,strlen(hash),info) + && ops_write("\r\n\r\n",4,info)); + + if (rtn==ops_false) + { + OPS_ERROR(&info->errors, OPS_E_W, "Error pushing clearsigned header"); + free(arg); + return rtn; + } + + arg->seen_nl=ops_true; + arg->sig=sig; + arg->trailing=ops_memory_new(); + ops_writer_push(info,dash_escaped_writer,NULL,dash_escaped_destroyer,arg); + return rtn; + } + + +/** + * \struct base64_arg_t + */ +typedef struct + { + unsigned pos; + unsigned char t; + unsigned checksum; + } base64_arg_t; + +static char b64map[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" +"0123456789+/"; + +static ops_boolean_t base64_writer(const unsigned char *src, + unsigned length,ops_error_t **errors, + ops_writer_info_t *winfo) + { + base64_arg_t *arg=ops_writer_get_arg(winfo); + unsigned n; + + for(n=0 ; n < length ; ) + { + arg->checksum=ops_crc24(arg->checksum,src[n]); + if(arg->pos == 0) + { + /* XXXXXX00 00000000 00000000 */ + if(!ops_stacked_write(&b64map[src[n] >> 2],1,errors,winfo)) + return ops_false; + + /* 000000XX xxxx0000 00000000 */ + arg->t=(src[n++]&3) << 4; + arg->pos=1; + } + else if(arg->pos == 1) + { + /* 000000xx XXXX0000 00000000 */ + arg->t+=src[n] >> 4; + if(!ops_stacked_write(&b64map[arg->t],1,errors,winfo)) + return ops_false; + + /* 00000000 0000XXXX xx000000 */ + arg->t=(src[n++]&0xf) << 2; + arg->pos=2; + } + else if(arg->pos == 2) + { + /* 00000000 0000xxxx XX000000 */ + arg->t+=src[n] >> 6; + if(!ops_stacked_write(&b64map[arg->t],1,errors,winfo)) + return ops_false; + + /* 00000000 00000000 00XXXXXX */ + if(!ops_stacked_write(&b64map[src[n++]&0x3f],1,errors,winfo)) + return ops_false; + + arg->pos=0; + } + } + + return ops_true; + } + +static ops_boolean_t signature_finaliser(ops_error_t **errors, + ops_writer_info_t *winfo) + { + base64_arg_t *arg=ops_writer_get_arg(winfo); + static char trailer[]="\r\n-----END PGP SIGNATURE-----\r\n"; + unsigned char c[3]; + + if(arg->pos) + { + if(!ops_stacked_write(&b64map[arg->t],1,errors,winfo)) + return ops_false; + if(arg->pos == 1 && !ops_stacked_write("==",2,errors,winfo)) + return ops_false; + if(arg->pos == 2 && !ops_stacked_write("=",1,errors,winfo)) + return ops_false; + } + /* Ready for the checksum */ + if(!ops_stacked_write("\r\n=",3,errors,winfo)) + return ops_false; + + arg->pos=0; /* get ready to write the checksum */ + + c[0]=arg->checksum >> 16; + c[1]=arg->checksum >> 8; + c[2]=arg->checksum; + /* push the checksum through our own writer */ + if(!base64_writer(c,3,errors,winfo)) + return ops_false; + + return ops_stacked_write(trailer,sizeof trailer-1,errors,winfo); + } + +/** + * \struct linebreak_arg_t + */ +typedef struct + { + unsigned pos; + } linebreak_arg_t; + +#define BREAKPOS 76 + +static ops_boolean_t linebreak_writer(const unsigned char *src, + unsigned length, + ops_error_t **errors, + ops_writer_info_t *winfo) + { + linebreak_arg_t *arg=ops_writer_get_arg(winfo); + unsigned n; + + for(n=0 ; n < length ; ++n,++arg->pos) + { + if(src[n] == '\r' || src[n] == '\n') + arg->pos=0; + + if(arg->pos == BREAKPOS) + { + if(!ops_stacked_write("\r\n",2,errors,winfo)) + return ops_false; + arg->pos=0; + } + if(!ops_stacked_write(&src[n],1,errors,winfo)) + return ops_false; + } + + return ops_true; + } + +/** + * \ingroup Core_WritersNext + * \brief Push armoured signature on stack + * \param info + */ +ops_boolean_t ops_writer_switch_to_armoured_signature(ops_create_info_t *info) + { + static char header[]="\r\n-----BEGIN PGP SIGNATURE-----\r\nVersion: " + OPS_VERSION_STRING "\r\n\r\n"; + base64_arg_t *base64; + + ops_writer_pop(info); + if (ops_write(header,sizeof header-1,info)==ops_false) + { + OPS_ERROR(&info->errors, OPS_E_W, "Error switching to armoured signature"); + return ops_false; + } + + ops_writer_push(info,linebreak_writer,NULL,ops_writer_generic_destroyer, + ops_mallocz(sizeof(linebreak_arg_t))); + + base64=ops_mallocz(sizeof *base64); + if (!base64) + { + OPS_MEMORY_ERROR(&info->errors); + return ops_false; + } + base64->checksum=CRC24_INIT; + ops_writer_push(info,base64_writer,signature_finaliser, + ops_writer_generic_destroyer,base64); + return ops_true; + } + +static ops_boolean_t armoured_message_finaliser(ops_error_t **errors, + ops_writer_info_t *winfo) + { + // TODO: This is same as signature_finaliser apart from trailer. + base64_arg_t *arg=ops_writer_get_arg(winfo); + static char trailer[]="\r\n-----END PGP MESSAGE-----\r\n"; + unsigned char c[3]; + + if(arg->pos) + { + if(!ops_stacked_write(&b64map[arg->t],1,errors,winfo)) + return ops_false; + if(arg->pos == 1 && !ops_stacked_write("==",2,errors,winfo)) + return ops_false; + if(arg->pos == 2 && !ops_stacked_write("=",1,errors,winfo)) + return ops_false; + } + /* Ready for the checksum */ + if(!ops_stacked_write("\r\n=",3,errors,winfo)) + return ops_false; + + arg->pos=0; /* get ready to write the checksum */ + + c[0]=arg->checksum >> 16; + c[1]=arg->checksum >> 8; + c[2]=arg->checksum; + /* push the checksum through our own writer */ + if(!base64_writer(c,3,errors,winfo)) + return ops_false; + + return ops_stacked_write(trailer,sizeof trailer-1,errors,winfo); + } + +/** + \ingroup Core_WritersNext + \brief Write a PGP MESSAGE + \todo replace with generic function +*/ +void ops_writer_push_armoured_message(ops_create_info_t *info) +// ops_create_signature_t *sig) + { + static char header[]="-----BEGIN PGP MESSAGE-----\r\n"; + + base64_arg_t *base64; + + ops_write(header,sizeof header-1,info); + ops_write("\r\n",2,info); + base64=ops_mallocz(sizeof *base64); + base64->checksum=CRC24_INIT; + ops_writer_push(info,base64_writer,armoured_message_finaliser,ops_writer_generic_destroyer,base64); + } + +static ops_boolean_t armoured_finaliser(ops_armor_type_t type, ops_error_t **errors, + ops_writer_info_t *winfo) + { + static char tail_public_key[]="\r\n-----END PGP PUBLIC KEY BLOCK-----\r\n"; + static char tail_private_key[]="\r\n-----END PGP PRIVATE KEY BLOCK-----\r\n"; + + char* tail=NULL; + unsigned int sz_tail=0; + + switch(type) + { + case OPS_PGP_PUBLIC_KEY_BLOCK: + tail=tail_public_key; + sz_tail=sizeof tail_public_key-1; + break; + + case OPS_PGP_PRIVATE_KEY_BLOCK: + tail=tail_private_key; + sz_tail=sizeof tail_private_key-1; + break; + + default: + assert(0); + } + + base64_arg_t *arg=ops_writer_get_arg(winfo); + unsigned char c[3]; + + if(arg->pos) + { + if(!ops_stacked_write(&b64map[arg->t],1,errors,winfo)) + return ops_false; + if(arg->pos == 1 && !ops_stacked_write("==",2,errors,winfo)) + return ops_false; + if(arg->pos == 2 && !ops_stacked_write("=",1,errors,winfo)) + return ops_false; + } + + /* Ready for the checksum */ + if(!ops_stacked_write("\r\n=",3,errors,winfo)) + return ops_false; + + arg->pos=0; /* get ready to write the checksum */ + + c[0]=arg->checksum >> 16; + c[1]=arg->checksum >> 8; + c[2]=arg->checksum; + /* push the checksum through our own writer */ + if(!base64_writer(c,3,errors,winfo)) + return ops_false; + + return ops_stacked_write(tail,sz_tail,errors,winfo); + } + +static ops_boolean_t armoured_public_key_finaliser(ops_error_t **errors, + ops_writer_info_t *winfo) + { + return armoured_finaliser(OPS_PGP_PUBLIC_KEY_BLOCK,errors,winfo); + } + +static ops_boolean_t armoured_private_key_finaliser(ops_error_t **errors, + ops_writer_info_t *winfo) + { + return armoured_finaliser(OPS_PGP_PRIVATE_KEY_BLOCK,errors,winfo); + } + +// \todo use this for other armoured types +/** + \ingroup Core_WritersNext + \brief Push Armoured Writer on stack (generic) +*/ +void ops_writer_push_armoured(ops_create_info_t *info, ops_armor_type_t type) + { + static char hdr_public_key[]="-----BEGIN PGP PUBLIC KEY BLOCK-----\r\nVersion: " + OPS_VERSION_STRING "\r\n\r\n"; + static char hdr_private_key[]="-----BEGIN PGP PRIVATE KEY BLOCK-----\r\nVersion: " + OPS_VERSION_STRING "\r\n\r\n"; + + char* header=NULL; + unsigned int sz_hdr=0; + ops_boolean_t (* finaliser)(ops_error_t **errors, ops_writer_info_t *winfo); + + switch(type) + { + case OPS_PGP_PUBLIC_KEY_BLOCK: + header=hdr_public_key; + sz_hdr=sizeof hdr_public_key-1; + finaliser=armoured_public_key_finaliser; + break; + + case OPS_PGP_PRIVATE_KEY_BLOCK: + header=hdr_private_key; + sz_hdr=sizeof hdr_private_key-1; + finaliser=armoured_private_key_finaliser; + break; + + default: + assert(0); + } + + ops_write(header,sz_hdr,info); + + ops_writer_push(info,linebreak_writer,NULL,ops_writer_generic_destroyer, + ops_mallocz(sizeof(linebreak_arg_t))); + + base64_arg_t *arg=ops_mallocz(sizeof *arg); + arg->checksum=CRC24_INIT; + ops_writer_push(info,base64_writer,finaliser,ops_writer_generic_destroyer,arg); + } + + +// EOF diff --git a/openpgpsdk/src/writer_encrypt.c b/openpgpsdk/src/writer_encrypt.c new file mode 100644 index 000000000..4224fca42 --- /dev/null +++ b/openpgpsdk/src/writer_encrypt.c @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + */ + +#include +#include +#include + +static int debug=0; + +typedef struct + { + ops_crypt_t* crypt; + int free_crypt; + } crypt_arg_t; + +/* + * This writer simply takes plaintext as input, + * encrypts it with the given key + * and outputs the resulting encrypted text + */ +static ops_boolean_t encrypt_writer(const unsigned char *src, + unsigned length, + ops_error_t **errors, + ops_writer_info_t *winfo) + { + +#define BUFSZ 1024 // arbitrary number + unsigned char encbuf[BUFSZ]; + unsigned remaining=length; + unsigned done=0; + + crypt_arg_t *arg=(crypt_arg_t *)ops_writer_get_arg(winfo); + + if (!ops_is_sa_supported(arg->crypt->algorithm)) + assert(0); // \todo proper error handling + + while (remaining) + { + unsigned len = remaining < BUFSZ ? remaining : BUFSZ; + // memcpy(buf,src,len); // \todo copy needed here? + + arg->crypt->cfb_encrypt(arg->crypt, encbuf, src+done, len); + + if (debug) + { + int i=0; + fprintf(stderr,"WRITING:\nunencrypted: "); + for (i=0; i<16; i++) + fprintf(stderr,"%2x ", src[done+i]); + fprintf(stderr,"\n"); + fprintf(stderr,"encrypted: "); + for (i=0; i<16; i++) + fprintf(stderr,"%2x ", encbuf[i]); + fprintf(stderr,"\n"); + } + + if (!ops_stacked_write(encbuf,len,errors,winfo)) + { + if (debug) + { fprintf(stderr, "encrypted_writer got error from stacked write, returning\n"); } + return ops_false; + } + remaining-=len; + done+=len; + } + + return ops_true; + } + +static void encrypt_destroyer (ops_writer_info_t *winfo) + + { + crypt_arg_t *arg=(crypt_arg_t *)ops_writer_get_arg(winfo); + if (arg->free_crypt) + free(arg->crypt); + free (arg); + } + +/** +\ingroup Core_WritersNext +\brief Push Encrypted Writer onto stack (create SE packets) +*/ +void ops_writer_push_encrypt_crypt(ops_create_info_t *cinfo, + ops_crypt_t *crypt) + { + // Create arg to be used with this writer + // Remember to free this in the destroyer + + crypt_arg_t *arg=ops_mallocz(sizeof *arg); + + // Setup the arg + + arg->crypt=crypt; + arg->free_crypt=0; + + // And push writer on stack + ops_writer_push(cinfo,encrypt_writer,NULL,encrypt_destroyer,arg); + + } + +// EOF diff --git a/openpgpsdk/src/writer_encrypt_se_ip.c b/openpgpsdk/src/writer_encrypt_se_ip.c new file mode 100644 index 000000000..438266da6 --- /dev/null +++ b/openpgpsdk/src/writer_encrypt_se_ip.c @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + */ + +#include +#include + +#ifndef WIN32 + #include +#endif + +#include + +#include "keyring_local.h" +#include +#include +#include +#include +#include +#include + +static int debug=0; + +typedef struct + { + ops_crypt_t* crypt; + } encrypt_se_ip_arg_t; + +static ops_boolean_t encrypt_se_ip_writer(const unsigned char *src, + unsigned length, + ops_error_t **errors, + ops_writer_info_t *winfo); +static void encrypt_se_ip_destroyer (ops_writer_info_t *winfo); + +// + +/** +\ingroup Core_WritersNext +\brief Push Encrypted SE IP Writer onto stack +*/ +void ops_writer_push_encrypt_se_ip(ops_create_info_t *cinfo, + const ops_keydata_t *pub_key) + { + ops_crypt_t* encrypt; + unsigned char *iv=NULL; + + // Create arg to be used with this writer + // Remember to free this in the destroyer + encrypt_se_ip_arg_t *arg=ops_mallocz(sizeof *arg); + + // Create and write encrypted PK session key + ops_pk_session_key_t* encrypted_pk_session_key; + encrypted_pk_session_key=ops_create_pk_session_key(pub_key); + ops_write_pk_session_key(cinfo,encrypted_pk_session_key); + + // Setup the arg + encrypt=ops_mallocz(sizeof *encrypt); + ops_crypt_any(encrypt, encrypted_pk_session_key->symmetric_algorithm); + iv=ops_mallocz(encrypt->blocksize); + encrypt->set_iv(encrypt, iv); + encrypt->set_key(encrypt, &encrypted_pk_session_key->key[0]); + ops_encrypt_init(encrypt); + + arg->crypt=encrypt; + + // And push writer on stack + ops_writer_push(cinfo,encrypt_se_ip_writer,NULL,encrypt_se_ip_destroyer,arg); + // tidy up + free(encrypted_pk_session_key); + free(iv); + } + +static ops_boolean_t encrypt_se_ip_writer(const unsigned char *src, + unsigned length, + ops_error_t **errors, + ops_writer_info_t *winfo) + { + encrypt_se_ip_arg_t *arg=ops_writer_get_arg(winfo); + + ops_boolean_t rtn=ops_true; + + ops_memory_t *mem_literal; + ops_create_info_t *cinfo_literal; + + ops_memory_t *mem_compressed; + ops_create_info_t *cinfo_compressed; + + ops_memory_t *my_mem; + ops_create_info_t *my_cinfo; + + const unsigned int bufsz=128; // initial value; gets expanded as necessary + ops_setup_memory_write(&cinfo_literal,&mem_literal,bufsz); + ops_setup_memory_write(&cinfo_compressed,&mem_compressed,bufsz); + ops_setup_memory_write(&my_cinfo,&my_mem,bufsz); + + // create literal data packet from source data + ops_write_literal_data_from_buf(src, length, OPS_LDT_BINARY, cinfo_literal); + assert(ops_memory_get_length(mem_literal)>length); + + // create compressed packet from literal data packet + ops_write_compressed(ops_memory_get_data(mem_literal), + ops_memory_get_length(mem_literal), + cinfo_compressed); + + // create SE IP packet set from this compressed literal data + ops_write_se_ip_pktset(ops_memory_get_data(mem_compressed), + ops_memory_get_length(mem_compressed), + arg->crypt, my_cinfo); + assert(ops_memory_get_length(my_mem)>ops_memory_get_length(mem_compressed)); + + // now write memory to next writer + rtn=ops_stacked_write(ops_memory_get_data(my_mem), + ops_memory_get_length(my_mem), + errors, winfo); + + ops_memory_free(my_mem); + ops_memory_free(mem_compressed); + ops_memory_free(mem_literal); + + return rtn; + } + +static void encrypt_se_ip_destroyer (ops_writer_info_t *winfo) + + { + encrypt_se_ip_arg_t *arg=ops_writer_get_arg(winfo); + + free(arg->crypt); + free(arg); + } + +ops_boolean_t ops_write_se_ip_pktset(const unsigned char *data, + const unsigned int len, + ops_crypt_t *crypt, + ops_create_info_t *cinfo) + { + unsigned char hashed[SHA_DIGEST_LENGTH]; + const size_t sz_mdc=1+1+SHA_DIGEST_LENGTH; + + size_t sz_preamble=crypt->blocksize+2; + unsigned char* preamble=ops_mallocz(sz_preamble); + + size_t sz_buf=sz_preamble+len+sz_mdc; + + ops_memory_t *mem_mdc; + ops_create_info_t *cinfo_mdc; + + if (!ops_write_ptag(OPS_PTAG_CT_SE_IP_DATA,cinfo) + || !ops_write_length(1+sz_buf,cinfo) + || !ops_write_scalar(SE_IP_DATA_VERSION,1,cinfo)) + { + free (preamble); + return 0; + } + + ops_random(preamble, crypt->blocksize); + preamble[crypt->blocksize]=preamble[crypt->blocksize-2]; + preamble[crypt->blocksize+1]=preamble[crypt->blocksize-1]; + + if (debug) + { + unsigned int i=0; + fprintf(stderr,"\npreamble: "); + for (i=0; i +#include +#include + +#include + +#include + +typedef struct + { + int fd; + } writer_fd_arg_t; + +static ops_boolean_t fd_writer(const unsigned char *src,unsigned length, + ops_error_t **errors, + ops_writer_info_t *winfo) + { + writer_fd_arg_t *arg=ops_writer_get_arg(winfo); + int n=write(arg->fd,src,length); + + if(n == -1) + { + OPS_SYSTEM_ERROR_1(errors,OPS_E_W_WRITE_FAILED,"write", + "file descriptor %d",arg->fd); + return ops_false; + } + + if((unsigned)n != length) + { + OPS_ERROR_1(errors,OPS_E_W_WRITE_TOO_SHORT, + "file descriptor %d",arg->fd); + return ops_false; + } + + return ops_true; + } + +static void fd_destroyer(ops_writer_info_t *winfo) + { + free(ops_writer_get_arg(winfo)); + } + +/** + * \ingroup Core_WritersFirst + * \brief Write to a File + * + * Set the writer in info to be a stock writer that writes to a file + * descriptor. If another writer has already been set, then that is + * first destroyed. + * + * \param info The info structure + * \param fd The file descriptor + * + */ + +void ops_writer_set_fd(ops_create_info_t *info,int fd) + { + writer_fd_arg_t *arg=malloc(sizeof *arg); + + arg->fd=fd; + ops_writer_set(info,fd_writer,NULL,fd_destroyer,arg); + } + +// EOF diff --git a/openpgpsdk/src/writer_memory.c b/openpgpsdk/src/writer_memory.c new file mode 100644 index 000000000..0c8fa57c4 --- /dev/null +++ b/openpgpsdk/src/writer_memory.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + */ + +#include +#include +#include +#include + +#include + +static ops_boolean_t memory_writer(const unsigned char *src,unsigned length, + ops_error_t **errors, + ops_writer_info_t *winfo) + { + ops_memory_t *mem=ops_writer_get_arg(winfo); + + OPS_USED(errors); + ops_memory_add(mem,src,length); + return ops_true; + } + +/** + * \ingroup Core_WritersFirst + * \brief Write to memory + * + * Set a memory writer. + * + * \param info The info structure + * \param mem The memory structure + * \note It is the caller's responsiblity to call ops_memory_free(mem) + * \sa ops_memory_free() + */ + +void ops_writer_set_memory(ops_create_info_t *info,ops_memory_t *mem) + { + ops_writer_set(info,memory_writer,NULL,NULL,mem); + } + + +// EOF diff --git a/openpgpsdk/src/writer_skey_checksum.c b/openpgpsdk/src/writer_skey_checksum.c new file mode 100644 index 000000000..6b8a480de --- /dev/null +++ b/openpgpsdk/src/writer_skey_checksum.c @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + */ + +#include + +#include + +//static int debug=0; + +typedef struct + { + ops_hash_algorithm_t hash_algorithm; + ops_hash_t hash; + unsigned char *hashed; + } skey_checksum_arg_t; + +static ops_boolean_t skey_checksum_writer(const unsigned char *src, const unsigned length, ops_error_t **errors, ops_writer_info_t *winfo) + { + skey_checksum_arg_t *arg=ops_writer_get_arg(winfo); + ops_boolean_t rtn=ops_true; + + // add contents to hash + arg->hash.add(&arg->hash, src, length); + + // write to next stacked writer + rtn=ops_stacked_write(src,length,errors,winfo); + + // tidy up and return + return rtn; + } + +static ops_boolean_t skey_checksum_finaliser(ops_error_t **errors __attribute__((unused)), ops_writer_info_t *winfo) + { + skey_checksum_arg_t *arg=ops_writer_get_arg(winfo); + arg->hash.finish(&arg->hash, arg->hashed); + return ops_true; + } + +static void skey_checksum_destroyer(ops_writer_info_t* winfo) + { + skey_checksum_arg_t *arg=ops_writer_get_arg(winfo); + free(arg); + } + +/** +\ingroup Core_WritersNext +\param cinfo +\param skey +*/ +void ops_push_skey_checksum_writer(ops_create_info_t *cinfo, ops_secret_key_t *skey) + { + // OPS_USED(info); + // XXX: push a SHA-1 checksum writer (and change s2k to 254). + skey_checksum_arg_t *arg=ops_mallocz(sizeof *arg); + + // configure the arg + arg->hash_algorithm=skey->hash_algorithm; + arg->hashed=&skey->checkhash[0]; + + // init the hash + ops_hash_any(&arg->hash, arg->hash_algorithm); + arg->hash.init(&arg->hash); + + ops_writer_push(cinfo, skey_checksum_writer, skey_checksum_finaliser, skey_checksum_destroyer, arg); + } + +// EOF diff --git a/openpgpsdk/src/writer_stream_encrypt_se_ip.c b/openpgpsdk/src/writer_stream_encrypt_se_ip.c new file mode 100644 index 000000000..08be27253 --- /dev/null +++ b/openpgpsdk/src/writer_stream_encrypt_se_ip.c @@ -0,0 +1,412 @@ +/* + * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted + * their moral rights under the UK Copyright Design and Patents Act 1988 to + * be recorded as the authors of this copyright work. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \file + */ + +#include +#include + +#ifndef WIN32 +#include +#endif + +#include + +#include "keyring_local.h" +#include +#include +#include +#include + +#define MAX_PARTIAL_DATA_LENGTH 1073741824 + +typedef struct + { + ops_crypt_t*crypt; + ops_memory_t *mem_data; + ops_memory_t *mem_literal; + ops_create_info_t *cinfo_literal; + ops_memory_t *mem_se_ip; + ops_create_info_t *cinfo_se_ip; + ops_hash_t hash; + } stream_encrypt_se_ip_arg_t; + + +static ops_boolean_t stream_encrypt_se_ip_writer(const unsigned char *src, + unsigned length, + ops_error_t **errors, + ops_writer_info_t *winfo); + +static ops_boolean_t stream_encrypt_se_ip_finaliser(ops_error_t** errors, + ops_writer_info_t* winfo); + +static void stream_encrypt_se_ip_destroyer (ops_writer_info_t *winfo); + +// + +/** +\ingroup Core_WritersNext +\param cinfo +\param pub_key +*/ +void ops_writer_push_stream_encrypt_se_ip(ops_create_info_t *cinfo, + const ops_keydata_t *pub_key) + { + ops_crypt_t* encrypt; + unsigned char *iv=NULL; + + const unsigned int bufsz=1024; // initial value; gets expanded as necessary + + // Create arg to be used with this writer + // Remember to free this in the destroyer + stream_encrypt_se_ip_arg_t *arg=ops_mallocz(sizeof *arg); + + // Create and write encrypted PK session key + ops_pk_session_key_t* encrypted_pk_session_key; + encrypted_pk_session_key=ops_create_pk_session_key(pub_key); + ops_write_pk_session_key(cinfo,encrypted_pk_session_key); + + // Setup the arg + encrypt=ops_mallocz(sizeof *encrypt); + ops_crypt_any(encrypt, encrypted_pk_session_key->symmetric_algorithm); + iv=ops_mallocz(encrypt->blocksize); + encrypt->set_iv(encrypt, iv); + encrypt->set_key(encrypt, &encrypted_pk_session_key->key[0]); + ops_encrypt_init(encrypt); + + arg->crypt=encrypt; + + arg->mem_data=ops_memory_new(); + ops_memory_init(arg->mem_data,bufsz); + + arg->mem_literal = NULL; + arg->cinfo_literal = NULL; + + ops_setup_memory_write(&arg->cinfo_se_ip, &arg->mem_se_ip, bufsz); + + // And push writer on stack + ops_writer_push(cinfo, + stream_encrypt_se_ip_writer, + stream_encrypt_se_ip_finaliser, + stream_encrypt_se_ip_destroyer,arg); + // tidy up + free(encrypted_pk_session_key); + free(iv); + } + + + +unsigned int ops_calc_partial_data_length(unsigned int len) + { + int i; + unsigned int mask = MAX_PARTIAL_DATA_LENGTH; + assert( len > 0 ); + + if ( len > MAX_PARTIAL_DATA_LENGTH ) { + return MAX_PARTIAL_DATA_LENGTH; + } + + for ( i = 0; i <= 30; i++ ) { + if ( mask & len) break; + mask >>= 1; + } + + return mask; + } + +ops_boolean_t ops_write_partial_data_length(unsigned int len, + ops_create_info_t *info) + { + // len must be a power of 2 from 0 to 30 + int i; + unsigned char c[1]; + + for ( i = 0; i <= 30; i++ ) { + if ( (len >> i) & 1) break; + } + + c[0] = 224 + i; + return ops_write(c,1,info); + } + +ops_boolean_t ops_stream_write_literal_data(const unsigned char *data, + unsigned int len, + ops_create_info_t *info) + { + while (len > 0) { + size_t pdlen = ops_calc_partial_data_length(len); + ops_write_partial_data_length(pdlen, info); + ops_write(data, pdlen, info); + data += pdlen; + len -= pdlen; + } + return ops_true; + } + +ops_boolean_t ops_stream_write_literal_data_first(const unsigned char *data, + unsigned int len, + const ops_literal_data_type_t type, + ops_create_info_t *info) + { + // \todo add filename + // \todo add date + // \todo do we need to check text data for line endings ? + + size_t sz_towrite = 1 + 1 + 4 + len; + size_t sz_pd = ops_calc_partial_data_length(sz_towrite); + assert(sz_pd >= 512); + + ops_write_ptag(OPS_PTAG_CT_LITERAL_DATA, info); + ops_write_partial_data_length(sz_pd, info); + ops_write_scalar(type, 1, info); + ops_write_scalar(0, 1, info); + ops_write_scalar(0, 4, info); + ops_write(data, sz_pd - 6, info); + + data += (sz_pd - 6); + sz_towrite -= sz_pd; + + ops_stream_write_literal_data(data, sz_towrite, info); + return ops_true; + } + +ops_boolean_t ops_stream_write_literal_data_last(const unsigned char *data, + unsigned int len, + ops_create_info_t *info) + { + ops_write_length(len, info); + ops_write(data, len, info); + return ops_true; + } + +ops_boolean_t ops_stream_write_se_ip(const unsigned char *data, + unsigned int len, + stream_encrypt_se_ip_arg_t *arg, + ops_create_info_t *cinfo) + { + while (len > 0) { + size_t pdlen = ops_calc_partial_data_length(len); + ops_write_partial_data_length(pdlen, cinfo); + + ops_writer_push_encrypt_crypt(cinfo, arg->crypt); + ops_write(data, pdlen, cinfo); + ops_writer_pop(cinfo); + + arg->hash.add(&arg->hash, data, pdlen); + + data += pdlen; + len -= pdlen; + } + return ops_true; + } + +ops_boolean_t ops_stream_write_se_ip_first(const unsigned char *data, + unsigned int len, + stream_encrypt_se_ip_arg_t *arg, + ops_create_info_t *cinfo) + { + size_t sz_preamble = arg->crypt->blocksize + 2; + size_t sz_towrite = sz_preamble + 1 + len; + unsigned char* preamble = ops_mallocz(sz_preamble); + + size_t sz_pd = ops_calc_partial_data_length(sz_towrite); + assert(sz_pd >= 512); + + ops_write_ptag(OPS_PTAG_CT_SE_IP_DATA, cinfo); + ops_write_partial_data_length(sz_pd, cinfo); + ops_write_scalar(SE_IP_DATA_VERSION, 1, cinfo); + + ops_writer_push_encrypt_crypt(cinfo, arg->crypt); + + ops_random(preamble, arg->crypt->blocksize); + preamble[arg->crypt->blocksize]=preamble[arg->crypt->blocksize-2]; + preamble[arg->crypt->blocksize+1]=preamble[arg->crypt->blocksize-1]; + + ops_hash_any(&arg->hash, OPS_HASH_SHA1); + arg->hash.init(&arg->hash); + + ops_write(preamble, sz_preamble, cinfo); + arg->hash.add(&arg->hash, preamble, sz_preamble); + + ops_write(data, sz_pd - sz_preamble - 1, cinfo); + arg->hash.add(&arg->hash, data, sz_pd - sz_preamble - 1); + + data += (sz_pd - sz_preamble -1); + sz_towrite -= sz_pd; + + ops_writer_pop(cinfo); + + ops_stream_write_se_ip(data, sz_towrite, arg, cinfo); + + free(preamble); + + return ops_true; + } + +ops_boolean_t ops_stream_write_se_ip_last(const unsigned char *data, + unsigned int len, + stream_encrypt_se_ip_arg_t *arg, + ops_create_info_t *cinfo) + { + unsigned char c[1]; + unsigned char hashed[SHA_DIGEST_LENGTH]; + const size_t sz_mdc = 1 + 1 + SHA_DIGEST_LENGTH; + size_t sz_buf = len + sz_mdc; + + ops_memory_t *mem_mdc; + ops_create_info_t *cinfo_mdc; + + arg->hash.add(&arg->hash, data, len); + + // MDC packet tag + c[0]=0xD3; + arg->hash.add(&arg->hash, &c[0], 1); + + // MDC packet len + c[0]=0x14; + arg->hash.add(&arg->hash, &c[0], 1); + + //finish + arg->hash.finish(&arg->hash, hashed); + + ops_setup_memory_write(&cinfo_mdc, &mem_mdc, sz_mdc); + ops_write_mdc(hashed, cinfo_mdc); + + // write length of last se_ip chunk + ops_write_length(sz_buf, cinfo); + + // encode everting + ops_writer_push_encrypt_crypt(cinfo, arg->crypt); + + ops_write(data, len, cinfo); + ops_write(ops_memory_get_data(mem_mdc), ops_memory_get_length(mem_mdc), cinfo); + + ops_writer_pop(cinfo); + + ops_teardown_memory_write(cinfo_mdc, mem_mdc); + + return ops_true; + } + +static ops_boolean_t stream_encrypt_se_ip_writer(const unsigned char *src, + unsigned length, + ops_error_t **errors, + ops_writer_info_t *winfo) + { + stream_encrypt_se_ip_arg_t *arg=ops_writer_get_arg(winfo); + + ops_boolean_t rtn=ops_true; + + if ( arg->cinfo_literal == NULL ) { // first literal data chunk is not yet written + size_t datalength; + + ops_memory_add(arg->mem_data,src,length); + datalength = ops_memory_get_length(arg->mem_data); + + // 4.2.2.4. Partial Body Lengths + // The first partial length MUST be at least 512 octets long. + if ( datalength < 512 ) { + return ops_true; // will wait for more data or end of stream + } + + ops_setup_memory_write(&arg->cinfo_literal,&arg->mem_literal,datalength+32); + ops_stream_write_literal_data_first(ops_memory_get_data(arg->mem_data), + datalength, + OPS_LDT_BINARY, + arg->cinfo_literal); + + ops_stream_write_se_ip_first(ops_memory_get_data(arg->mem_literal), + ops_memory_get_length(arg->mem_literal), + arg, arg->cinfo_se_ip); + } else { + ops_stream_write_literal_data(src, length, arg->cinfo_literal); + ops_stream_write_se_ip(ops_memory_get_data(arg->mem_literal), + ops_memory_get_length(arg->mem_literal), + arg, arg->cinfo_se_ip); + } + + // now write memory to next writer + rtn=ops_stacked_write(ops_memory_get_data(arg->mem_se_ip), + ops_memory_get_length(arg->mem_se_ip), + errors, winfo); + + ops_memory_clear(arg->mem_literal); + ops_memory_clear(arg->mem_se_ip); + + return rtn; + } + +static ops_boolean_t stream_encrypt_se_ip_finaliser(ops_error_t** errors, + ops_writer_info_t* winfo) + { + stream_encrypt_se_ip_arg_t *arg=ops_writer_get_arg(winfo); + // write last chunk of data + + if ( arg->cinfo_literal == NULL ) { + // first literal data chunk was not written + // so we know the total length of data, write a simple packet + + // create literal data packet from buffered data + ops_setup_memory_write(&arg->cinfo_literal, + &arg->mem_literal, + ops_memory_get_length(arg->mem_data)+32); + + ops_write_literal_data_from_buf(ops_memory_get_data(arg->mem_data), + ops_memory_get_length(arg->mem_data), + OPS_LDT_BINARY, arg->cinfo_literal); + + // create SE IP packet set from this literal data + ops_write_se_ip_pktset(ops_memory_get_data(arg->mem_literal), + ops_memory_get_length(arg->mem_literal), + arg->crypt, arg->cinfo_se_ip); + + } else { + // finish writing + ops_stream_write_literal_data_last(NULL, 0, arg->cinfo_literal); + ops_stream_write_se_ip_last(ops_memory_get_data(arg->mem_literal), + ops_memory_get_length(arg->mem_literal), + arg, arg->cinfo_se_ip); + } + + // now write memory to next writer + return ops_stacked_write(ops_memory_get_data(arg->mem_se_ip), + ops_memory_get_length(arg->mem_se_ip), + errors, winfo); + } + +static void stream_encrypt_se_ip_destroyer (ops_writer_info_t *winfo) + + { + stream_encrypt_se_ip_arg_t *arg=ops_writer_get_arg(winfo); + + ops_memory_free(arg->mem_data); + ops_teardown_memory_write(arg->cinfo_literal, arg->mem_literal); + ops_teardown_memory_write(arg->cinfo_se_ip, arg->mem_se_ip); + + arg->crypt->decrypt_finish(arg->crypt); + + free(arg->crypt); + free(arg); + } + + +// EOF