diff --git a/NEWS b/NEWS index b85bb747..177b4d20 100644 --- a/NEWS +++ b/NEWS @@ -78,6 +78,10 @@ Noteworthy changes in version 0.4.1 (unreleased) GpgmeImportResult and GpgmeImportStatus objects. Thus, the gpgme_op_import_ext variant is deprecated. + * The new gpgme_op_sign_result function provides detailed information + about the result of a signing operation in GpgmeSignResult, + GpgmeInvalidUserID and GpgmeNewSignature objects. + * Interface changes relative to the 0.4.0 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GpgmeIOCb CHANGED: Return type from void to GpgmeError. @@ -108,6 +112,14 @@ gpgme_op_import_ext DEPRECATED: Use gpgme_op_import_result. gpgme_op_import_result NEW GpgmeImportStatus NEW GpgmeImportResult NEW +GpgmePubKeyAlgo NEW +GpgmeHashAlgo NEW +GpgmeInvalidUserID NEW +GpgmeNewSignature NEW +GpgmeSignResult NEW +gpgme_op_sign_result NEW +gpgme_pubkey_algo_name NEW +gpgme_hash_algo_name NEW ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Noteworthy changes in version 0.4.0 (2002-12-23) diff --git a/doc/ChangeLog b/doc/ChangeLog index 24031a4b..0edda75b 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,5 +1,10 @@ 2003-04-27 Marcus Brinkmann + * gpgme.texi (Creating a Signature): Add info about + GpgmeNewSignature, GpgmeSignResult and gpgme_op_sign_result. + (Crypto Operations): Add GpgmeInvalidUserID. + (Algorithms): New chapter. + * gpgme.texi (Deleting Keys): Document GPGME_Ambiguous_Specification. (Error Values): Remove GPGME_Invalid_Type and GPGME_Invalid_Mode. diff --git a/doc/gpgme.texi b/doc/gpgme.texi index 7fdf41ee..e1c8c856 100644 --- a/doc/gpgme.texi +++ b/doc/gpgme.texi @@ -73,6 +73,7 @@ This is Edition @value{EDITION}, last updated @value{UPDATED}, of * Introduction:: How to use this manual. * Preparation:: What you should do before using the library. * Protocols and Engines:: Supported crypto protocols. +* Algorithms:: Supported algorithms. * Error Handling:: Error numbers and their meanings. * Exchanging Data:: Passing data to and from @acronym{GPGME}. * Contexts:: Handling @acronym{GPGME} contexts. @@ -114,6 +115,11 @@ Protocols and Engines * OpenPGP:: Support for the OpenPGP protocol. * Cryptographic Message Syntax:: Support for the CMS. +Algorithms + +* Public Key Algorithms:: A list of all public key algorithms. +* Hash Algorithms:: A list of all hash algorithms. + Error Handling * Error Values:: A list of all error values used. @@ -728,6 +734,110 @@ GnuPG. The @acronym{CMS} protocol is specified by @code{GPGME_PROTOCOL_CMS}. +@node Algorithms +@chapter Algorithms +@cindex algorithms + +The crypto backends support a variety of algorithms used in public key +cryptography. The following sections list the identifiers used to +denote such an algorithm. + +@menu +* Public Key Algorithms:: A list of all public key algorithms. +* Hash Algorithms:: A list of all hash algorithms. +@end menu + + +@node Public Key Algorithms +@section Public Key Algorithms +@cindex algorithms, public key +@cindex public key algorithms + +Public key algorithms are used for encryption, decryption, signing and +verification of signatures. + +@deftp {Data type} {enum GpgmePubKeyAlgo} +@tindex GpgmePubKeyAlgo +The @code{GpgmePubKeyAlgo} type specifies the set of all public key +algorithms that are supported by @acronym{GPGME}. Possible values +are: + +@table @code +@item GPGME_PK_RSA +This value indicates the RSA (Rivest, Shamir, Adleman) algorithm. + +@item GPGME_PK_RSA_E +Deprecated. This value indicates the RSA (Rivest, Shamir, Adleman) +algorithm for encryption and decryption only. + +@item GPGME_PK_RSA_S +Deprecated. This value indicates the RSA (Rivest, Shamir, Adleman) +algorithm for signing and verification only. + +@item GPGME_PK_DSA +This value indicates DSA, the Digital Signature Algorithm. + +@item GPGME_PK_ELG +This value indicates ElGamal. + +@item GPGME_PK_ELG_E +This value also indicates ElGamal and is used specifically in GnuPG. +@end table +@end deftp + +@deftypefun {const char *} gpgme_pubkey_algo_name (@w{GpgmePubKeyAlgo @var{algo}}) +The function @code{gpgme_pubkey_algo_name} returns a pointer to a +statically allocated string containing a description of the public key +algorithm @var{algo}. This string can be used to output the name of +the public key algorithm to the user. + +If @var{algo} is not a valid public key algorithm, @code{NULL} is +returned. +@end deftypefun + + +@node Hash Algorithms +@section Hash Algorithms +@cindex algorithms, hash +@cindex algorithms, message digest +@cindex hash algorithms +@cindex message digest algorithms + +Hash (message digest) algorithms are used to compress a long message +to make it suitable for public key cryptography. + +@deftp {Data type} {enum GpgmeHashAlgo} +@tindex GpgmeHashAlgo +The @code{GpgmeHashAlgo} type specifies the set of all hash algorithms +that are supported by @acronym{GPGME}. Possible values are: + +@table @code +@item GPGME_MD_MD5 +@item GPGME_MD_SHA1 +@item GPGME_MD_RMD160 +@item GPGME_MD_MD2 +@item GPGME_MD_TIGER +@item GPGME_MD_HAVAL +@item GPGME_MD_SHA256 +@item GPGME_MD_SHA384 +@item GPGME_MD_SHA512 +@item GPGME_MD_MD4 +@item GPGME_MD_CRC32 +@item GPGME_MD_CRC32_RFC1510 +@item GPGME_MD_CRC24_RFC2440 +@end table +@end deftp + +@deftypefun {const char *} gpgme_hash_algo_name (@w{GpgmeHashAlgo @var{algo}}) +The function @code{gpgme_hash_algo_name} returns a pointer to a +statically allocated string containing a description of the hash +algorithm @var{algo}. This string can be used to output the name of +the hash algorithm to the user. + +If @var{algo} is not a valid hash algorithm, @code{NULL} is returned. +@end deftypefun + + @node Error Handling @chapter Error Handling @cindex error handling @@ -2308,6 +2418,9 @@ The function @code{gpgme_op_import} adds the keys in the data buffer The format of @var{keydata} can be @var{ASCII} armored, for example, but the details are specific to the crypto engine. +After the operation completed successfully, the result can be +retrieved with @code{gpgme_op_import_result}. + The function returns @code{GPGME_No_Error} if the import was completed successfully, @code{GPGME_Invalid_Value} if @var{keydata} if @var{ctx} or @var{keydata} is not a valid pointer, and @code{GPGME_No_Data} if @@ -2333,7 +2446,8 @@ import. The structure contains the following members: @table @code @item GpgmeImportStatus next -This is a pointer to the next status object in the list. +This is a pointer to the next status structure in the linked list, or +@code{NULL} if this is the last element. @item char *fpr This is the fingerprint of the key that was considered. @@ -2592,10 +2706,34 @@ The function @code{gpgme_trust_item_release} destroys a @code{GpgmeTrustItem} object and releases all associated resources. @end deftypefun + @node Crypto Operations @section Crypto Operations @cindex cryptographic operation +Sometimes, the result of a crypto operation returns a list of invalid +user IDs encountered in processing the request. The following +structure is used to hold information about such an user ID. + +@deftp {Data type} {GpgmeInvalidUserID} +This is a pointer to a structure used to store a part of the result of +a crypto operation which takes user IDs as one input parameter. The +structure contains the following members: + +@table @code +@item GpgmeInvalidUserID next +This is a pointer to the next invalid user ID structure in the linked +list, or @code{NULL} if this is the last element. + +@item char *id +The invalid user ID encountered. + +@item GpgmeError reason +An error code describing the reason why the user ID was found invalid. +@end table +@end deftp + + @menu * Decrypt:: Decrypting a ciphertext. * Verify:: Verifying a signature. @@ -2994,8 +3132,8 @@ the data object @var{plain} and returns it in the data object @acronym{ASCII} armor and text mode attributes set for the context @var{ctx} and the requested signature mode @var{mode}. -More information about the signatures is available with -@code{gpgme_get_op_info}. @xref{Detailed Results}. +After the operation completed successfully, the result can be +retrieved with @code{gpgme_op_sign_result}. If an S/MIME signed message is created using the CMS crypto engine, the number of certificates to include in the message can be specified @@ -3020,6 +3158,63 @@ started successfully, and @code{GPGME_Invalid_Value} if @var{ctx}, @var{plain} or @var{sig} is not a valid pointer. @end deftypefun +@deftp {Data type} {GpgmeNewSignature} +This is a pointer to a structure used to store a part of the result of +a @code{gpgme_op_sign} operation. The structure contains the +following members: + +@table @code +@item GpgmeNewSignature next +This is a pointer to the next new signature structure in the linked +list, or @code{NULL} if this is the last element. + +@item GpgmeSigMode type +The type of this signature. + +@item GpgmePubKeyAlgo +The public key algorithm used to create this signature. + +@item GpgmeHashAlgo +The hash algorithm used to create this signature. + +@item unsigned long class +The signature class of this signature. + +@item long int created +The creation timestamp of this signature. + +@item char *fpr +The fingerprint of the key which was used to create this signature. +@end table +@end deftp + +@deftp {Data type} {GpgmeSignResult} +This is a pointer to a structure used to store the result of a +@code{gpgme_op_sign} operation. After successfully generating a +signature, you can retrieve the pointer to the result with +@code{gpgme_op_sign_result}. The structure contains the following +members: + +@table @code +@item GpgmeInvalidUserID invalid_signers +A linked list with information about all invalid user IDs for which a +signature could not be created. + +@item GpgmeNewSignature signatures +A linked list with information about all signatures created. +@end table +@end deftp + +@deftypefun GpgmeSignResult gpgme_op_sign_result (@w{GpgmeCtx @var{ctx}}) +The function @code{gpgme_op_sign_result} returns a +@code{GpgmeSignResult} pointer to a structure holding the result of a +@code{gpgme_op_sign} operation. The pointer is only valid if the last +operation on the context was a @code{gpgme_op_sign} or +@code{gpgme_op_sign_start} operation, and if this operation finished +successfully. The returned pointer is only valid until the next +operation is started on the context. +@end deftypefun + @node Encrypt @subsection Encrypt diff --git a/gpgme/ChangeLog b/gpgme/ChangeLog index 774d9307..8716eda2 100644 --- a/gpgme/ChangeLog +++ b/gpgme/ChangeLog @@ -1,5 +1,36 @@ 2003-04-27 Marcus Brinkmann + * gpgme.h (GpgmePubKeyAlgo, GpgmeHashAlgo, GpgmeInvalidUserID, + GpgmeNewSignature, GpgmeSignResult): New data types. + (gpgme_op_sign_result, gpgme_pubkey_algo_name, + gpgme_hash_algo_name): New prototypes. + * gpgme.c (gpgme_pubkey_algo_name): New function. + (gpgme_hash_algo_name): Likewise. + * ops.h (_gpgme_parse_inv_userid, _gpgme_op_sign_init_result): New + prototype. + (_gpgme_op_sign_status_handler): Fix prototype. + * op-support.c: Include and . + (_gpgme_parse_inv_userid): New function. + * sign.c: Include and "gpgme.h", but not , + and "util.h". + (SKIP_TOKEN_OR_RETURN): Remove macro. + (struct sign_result): Change to op_data_t type and rework it. + (release_sign_result): Rename to ... + (release_op_data): ... this and rewrite it. + (append_xml_info): Remove function. + (gpgme_op_sign_result): New function. + (parse_sig_created): New function. + (_gpgme_sign_status_handler): Change first argument to void *. + Rewrite the function to use the new result structure and functions. + (_gpgme_op_sign_init_result): New function. + (_gpgme_op_sign_start): Rename to ... + (sign_start): ... this. Call _gpgme_op_sign_init_result. + (gpgme_op_sign_start): Use sign_start instead _gpgme_op_sign_start. + (gpgme_op_sign): Likewise. + + * encrypt-sign.c (_gpgme_op_encrypt_sign_start): Call + _gpgme_op_sign_init_result. + * delete.c: Include and "gpgme.h", but not "util.h" or "key.h". (enum delete_problem): Move into function delete_status_handler. diff --git a/gpgme/encrypt-sign.c b/gpgme/encrypt-sign.c index 68ab115c..3e28773a 100644 --- a/gpgme/encrypt-sign.c +++ b/gpgme/encrypt-sign.c @@ -51,6 +51,10 @@ _gpgme_op_encrypt_sign_start (GpgmeCtx ctx, int synchronous, if (err) return err; + err = _gpgme_op_sign_init_result (ctx); + if (err) + return err; + if (!plain) return GPGME_No_Data; if (!cipher) diff --git a/gpgme/gpgme.c b/gpgme/gpgme.c index 41b3b095..72f502a2 100644 --- a/gpgme/gpgme.c +++ b/gpgme/gpgme.c @@ -521,3 +521,82 @@ gpgme_get_io_cbs (GpgmeCtx ctx, struct GpgmeIOCbs *io_cbs) if (ctx && io_cbs) *io_cbs = ctx->io_cbs; } + + +const char * +gpgme_pubkey_algo_name (GpgmePubKeyAlgo algo) +{ + switch (algo) + { + case GPGME_PK_RSA: + return "RSA"; + + case GPGME_PK_RSA_E: + return "RSA-E"; + + case GPGME_PK_RSA_S: + return "RSA-S"; + + case GPGME_PK_ELG_E: + return "ELG-E"; + + case GPGME_PK_DSA: + return "DSA"; + + case GPGME_PK_ELG: + return "ELG"; + + default: + return NULL; + } +} + + +const char * +gpgme_hash_algo_name (GpgmeHashAlgo algo) +{ + switch (algo) + { + case GPGME_MD_MD5: + return "MD5"; + + case GPGME_MD_SHA1: + return "SHA1"; + + case GPGME_MD_RMD160: + return "RMD160"; + + case GPGME_MD_MD2: + return "MD2"; + + case GPGME_MD_TIGER: + return "TIGER"; + + case GPGME_MD_HAVAL: + return "HAVAL"; + + case GPGME_MD_SHA256: + return "SHA256"; + + case GPGME_MD_SHA384: + return "SHA384"; + + case GPGME_MD_SHA512: + return "SHA512"; + + case GPGME_MD_MD4: + return "MD4"; + + case GPGME_MD_CRC32: + return "CRC32"; + + case GPGME_MD_CRC32_RFC1510: + return "CRC32-RFC1510"; + + case GPGME_MD_CRC24_RFC2440: + return "CRC24-RFC2440"; + + default: + return NULL; + } +} diff --git a/gpgme/gpgme.h b/gpgme/gpgme.h index 7334bfce..9fa2af01 100644 --- a/gpgme/gpgme.h +++ b/gpgme/gpgme.h @@ -138,6 +138,41 @@ typedef enum } GpgmeDataEncoding; + +/* Public key algorithms from libgcrypt. */ +typedef enum + { + GPGME_PK_RSA = 1, + GPGME_PK_RSA_E = 2, + GPGME_PK_RSA_S = 3, + GPGME_PK_ELG_E = 16, + GPGME_PK_DSA = 17, + GPGME_PK_ELG = 20 + } +GpgmePubKeyAlgo; + + +/* Hash algorithms from libgcrypt. */ +typedef enum + { + GPGME_MD_NONE = 0, + GPGME_MD_MD5 = 1, + GPGME_MD_SHA1 = 2, + GPGME_MD_RMD160 = 3, + GPGME_MD_MD2 = 5, + GPGME_MD_TIGER = 6, /* TIGER/192. */ + GPGME_MD_HAVAL = 7, /* HAVAL, 5 pass, 160 bit. */ + GPGME_MD_SHA256 = 8, + GPGME_MD_SHA384 = 9, + GPGME_MD_SHA512 = 10, + GPGME_MD_MD4 = 301, + GPGME_MD_CRC32 = 302, + GPGME_MD_CRC32_RFC1510 = 303, + GPGME_MD_CRC24_RFC2440 = 304 + } +GpgmeHashAlgo; + + /* The possible signature stati. */ typedef enum { @@ -426,6 +461,16 @@ void gpgme_set_progress_cb (GpgmeCtx c, GpgmeProgressCb cb, void *hook_value); void gpgme_get_progress_cb (GpgmeCtx ctx, GpgmeProgressCb *cb, void **hook_value); + +/* Return a statically allocated string with the name of the public + key algorithm ALGO, or NULL if that name is not known. */ +const char *gpgme_pubkey_algo_name (GpgmePubKeyAlgo algo); + +/* Return a statically allocated string with the name of the hash + algorithm ALGO, or NULL if that name is not known. */ +const char *gpgme_hash_algo_name (GpgmeHashAlgo algo); + + /* Delete all signers from CTX. */ void gpgme_signers_clear (GpgmeCtx ctx); @@ -710,10 +755,18 @@ const char *gpgme_trust_item_get_string_attr (GpgmeTrustItem item, attribute appears more than once in the key. */ int gpgme_trust_item_get_int_attr (GpgmeTrustItem item, GpgmeAttr what, const void *reserved, int idx); + +/* Crypto Operations. */ +struct _gpgme_invalid_user_id +{ + struct _gpgme_invalid_user_id *next; + char *id; + GpgmeError reason; +}; +typedef struct _gpgme_invalid_user_id *GpgmeInvalidUserID; -/* Crypto operation function. */ - + /* Encrypt plaintext PLAIN within CTX for the recipients RECP and store the resulting ciphertext in CIPHER. */ GpgmeError gpgme_op_encrypt_start (GpgmeCtx ctx, @@ -747,8 +800,32 @@ GpgmeError gpgme_op_decrypt_verify_start (GpgmeCtx ctx, GpgmeError gpgme_op_decrypt_verify (GpgmeCtx ctx, GpgmeData cipher, GpgmeData plain); -/* Sign the plaintext PLAIN and store the signature in SIG. Only - detached signatures are supported for now. */ + +/* Signing. */ +struct _gpgme_new_signature +{ + struct _gpgme_new_signature *next; + GpgmeSigMode type; + GpgmePubKeyAlgo pubkey_algo; + GpgmeHashAlgo hash_algo; + unsigned long class; + long int created; + char *fpr; +}; +typedef struct _gpgme_new_signature *GpgmeNewSignature; + +struct _gpgme_op_sign_result +{ + /* The list of invalid signers. */ + GpgmeInvalidUserID invalid_signers; + GpgmeNewSignature signatures; +}; +typedef struct _gpgme_op_sign_result *GpgmeSignResult; + +/* Retrieve a pointer to the result of the signing operation. */ +GpgmeSignResult gpgme_op_sign_result (GpgmeCtx ctx); + +/* Sign the plaintext PLAIN and store the signature in SIG. */ GpgmeError gpgme_op_sign_start (GpgmeCtx ctx, GpgmeData plain, GpgmeData sig, GpgmeSigMode mode); @@ -756,6 +833,7 @@ GpgmeError gpgme_op_sign (GpgmeCtx ctx, GpgmeData plain, GpgmeData sig, GpgmeSigMode mode); + /* Verify within CTX that SIG is a valid signature for TEXT. */ GpgmeError gpgme_op_verify_start (GpgmeCtx ctx, GpgmeData sig, GpgmeData signed_text, GpgmeData plaintext); diff --git a/gpgme/op-support.c b/gpgme/op-support.c index 1d55e387..079bd67e 100644 --- a/gpgme/op-support.c +++ b/gpgme/op-support.c @@ -21,6 +21,8 @@ #include #endif #include +#include +#include #include "gpgme.h" #include "context.h" @@ -105,3 +107,79 @@ _gpgme_op_reset (GpgmeCtx ctx, int type) _gpgme_engine_set_io_cbs (ctx->engine, &io_cbs); return err; } + + +GpgmeError +_gpgme_parse_inv_userid (char *args, GpgmeInvalidUserID *userid) +{ + GpgmeInvalidUserID inv_userid; + char *tail; + long int reason; + + inv_userid = malloc (sizeof (*inv_userid)); + if (!inv_userid) + return GPGME_Out_Of_Core; + inv_userid->next = NULL; + errno = 0; + reason = strtol (args, &tail, 0); + if (errno || args == tail || *tail != ' ') + { + /* The crypto backend does not behave. */ + free (inv_userid); + return GPGME_General_Error; + } + + switch (reason) + { + default: + case 0: + inv_userid->reason = GPGME_Unknown_Reason; + + case 1: + inv_userid->reason = GPGME_Not_Found; + + case 2: + inv_userid->reason = GPGME_Ambiguous_Specification; + + case 3: + inv_userid->reason = GPGME_Wrong_Key_Usage; + + case 4: + inv_userid->reason = GPGME_Key_Revoked; + + case 5: + inv_userid->reason = GPGME_Key_Expired; + + case 6: + inv_userid->reason = GPGME_No_CRL_Known; + + case 7: + inv_userid->reason = GPGME_CRL_Too_Old; + + case 8: + inv_userid->reason = GPGME_Policy_Mismatch; + + case 9: + inv_userid->reason = GPGME_No_Secret_Key; + + case 10: + inv_userid->reason = GPGME_Key_Not_Trusted; + } + + while (*tail == ' ') + tail++; + if (*tail) + { + inv_userid->id = strdup (tail); + if (!inv_userid->id) + { + free (inv_userid); + return GPGME_Out_Of_Core; + } + } + else + inv_userid->id = NULL; + + *userid = inv_userid; + return 0; +} diff --git a/gpgme/ops.h b/gpgme/ops.h index 61ec8d74..7cbb9425 100644 --- a/gpgme/ops.h +++ b/gpgme/ops.h @@ -57,12 +57,22 @@ GpgmeError _gpgme_data_outbound_handler (void *opaque, int fd); GpgmeError _gpgme_key_new ( GpgmeKey *r_key ); GpgmeError _gpgme_key_new_secret ( GpgmeKey *r_key ); -/*-- op-support.c --*/ + +/* From op-support.c. */ + +/* Find or create the op data object of type TYPE. */ GpgmeError _gpgme_op_data_lookup (GpgmeCtx ctx, ctx_op_data_type type, void **hook, int size, void (*cleanup) (void *)); + +/* Prepare a new operation on CTX. */ GpgmeError _gpgme_op_reset (GpgmeCtx ctx, int synchronous); +/* Parse the invalid user ID status line in ARGS and return the result + in USERID. */ +GpgmeError _gpgme_parse_inv_userid (char *args, GpgmeInvalidUserID *userid); + + /*-- verify.c --*/ GpgmeError _gpgme_verify_status_handler (GpgmeCtx ctx, GpgmeStatusCode code, char *args); @@ -74,10 +84,18 @@ GpgmeError _gpgme_decrypt_start (GpgmeCtx ctx, int synchronous, GpgmeData ciph, GpgmeData plain, void *status_handler); -/*-- sign.c --*/ -GpgmeError _gpgme_sign_status_handler (GpgmeCtx ctx, GpgmeStatusCode code, + +/* From sign.c. */ + +/* Create an initial op data object for signing. Needs to be called + once before calling _gpgme_sign_status_handler. */ +GpgmeError _gpgme_op_sign_init_result (GpgmeCtx ctx); + +/* Process a status line for signing operations. */ +GpgmeError _gpgme_sign_status_handler (void *priv, GpgmeStatusCode code, char *args); + /*-- encrypt.c --*/ GpgmeError _gpgme_encrypt_status_handler (GpgmeCtx ctx, GpgmeStatusCode code, char *args); diff --git a/gpgme/sign.c b/gpgme/sign.c index f416d104..260aa5f7 100644 --- a/gpgme/sign.c +++ b/gpgme/sign.c @@ -1,4 +1,4 @@ -/* sign.c - signing functions +/* sign.c - Signing function. Copyright (C) 2000 Werner Koch (dd9jn) Copyright (C) 2001, 2002, 2003 g10 Code GmbH @@ -21,157 +21,203 @@ #if HAVE_CONFIG_H #include #endif -#include #include #include -#include +#include -#include "util.h" +#include "gpgme.h" #include "context.h" #include "ops.h" -#define SKIP_TOKEN_OR_RETURN(a) do { \ - while (*(a) && *(a) != ' ') (a)++; \ - while (*(a) == ' ') (a)++; \ - if (!*(a)) \ - return; /* oops */ \ -} while (0) - -struct sign_result + +typedef struct { - int okay; - GpgmeData xmlinfo; -}; -typedef struct sign_result *SignResult; + struct _gpgme_op_sign_result result; + + /* A pointer to the next pointer of the last invalid signer in + the list. This makes appending new invalid signers painless + while preserving the order. */ + GpgmeInvalidUserID *last_signer_p; + + /* Likewise for signature information. */ + GpgmeNewSignature *last_sig_p; +} *op_data_t; static void -release_sign_result (void *hook) +release_op_data (void *hook) { - SignResult result = (SignResult) hook; + op_data_t opd = (op_data_t) hook; + GpgmeInvalidUserID invalid_signer = opd->result.invalid_signers; + GpgmeNewSignature sig = opd->result.signatures; - gpgme_data_release (result->xmlinfo); + while (invalid_signer) + { + GpgmeInvalidUserID next = invalid_signer->next; + free (invalid_signer->id); + free (invalid_signer); + invalid_signer = next; + } + + while (sig) + { + GpgmeNewSignature next = sig->next; + free (sig->fpr); + free (sig); + sig = next; + } } -/* Parse the args and save the information - - in an XML structure. With args of NULL the xml structure is - closed. */ -static void -append_xml_siginfo (GpgmeData *rdh, char *args) + +GpgmeSignResult +gpgme_op_sign_result (GpgmeCtx ctx) { - GpgmeData dh; - char helpbuf[100]; - int i; - char *s; - unsigned long ul; - - if (!*rdh) - { - if (gpgme_data_new (rdh)) - { - return; /* fixme: We are ignoring out-of-core */ - } - dh = *rdh; - _gpgme_data_append_string (dh, "\n"); - } - else - { - dh = *rdh; - _gpgme_data_append_string (dh, " \n"); - } - - if (!args) - { - /* Just close the XML containter. */ - _gpgme_data_append_string (dh, "\n"); - return; - } - - _gpgme_data_append_string (dh, " \n"); - - _gpgme_data_append_string (dh, - *args == 'D' ? " \n" : - *args == 'C' ? " \n" : - *args == 'S' ? " \n" : ""); - SKIP_TOKEN_OR_RETURN (args); - - sprintf (helpbuf, " %d\n", atoi (args)); - _gpgme_data_append_string (dh, helpbuf); - SKIP_TOKEN_OR_RETURN (args); - - i = atoi (args); - sprintf (helpbuf, " %d\n", atoi (args)); - _gpgme_data_append_string (dh, helpbuf); - switch (i) - { - case 1: s = "pgp-md5"; break; - case 2: s = "pgp-sha1"; break; - case 3: s = "pgp-ripemd160"; break; - case 5: s = "pgp-md2"; break; - case 6: s = "pgp-tiger192"; break; - case 7: s = "pgp-haval-5-160"; break; - case 8: s = "pgp-sha256"; break; - case 9: s = "pgp-sha384"; break; - case 10: s = "pgp-sha512"; break; - default: s = "pgp-unknown"; break; - } - sprintf (helpbuf, " %s\n", s); - _gpgme_data_append_string (dh,helpbuf); - SKIP_TOKEN_OR_RETURN (args); - - sprintf (helpbuf, " %.2s\n", args); - _gpgme_data_append_string (dh, helpbuf); - SKIP_TOKEN_OR_RETURN (args); - - ul = strtoul (args, NULL, 10); - sprintf (helpbuf, " %lu\n", ul); - _gpgme_data_append_string (dh, helpbuf); - SKIP_TOKEN_OR_RETURN (args); - - /* Count the length of the finperprint. */ - for (i = 0; args[i] && args[i] != ' '; i++) - ; - _gpgme_data_append_string (dh, " "); - _gpgme_data_append (dh, args, i); - _gpgme_data_append_string (dh, "\n"); -} - -GpgmeError -_gpgme_sign_status_handler (GpgmeCtx ctx, GpgmeStatusCode code, char *args) -{ - SignResult result; + op_data_t opd; GpgmeError err; - err = _gpgme_passphrase_status_handler (ctx, code, args); + err = _gpgme_op_data_lookup (ctx, OPDATA_SIGN, (void **) &opd, -1, NULL); + if (err || !opd) + return NULL; + + return &opd->result; +} + + +static GpgmeError +parse_sig_created (char *args, GpgmeNewSignature *sigp) +{ + GpgmeNewSignature sig; + char *tail; + + sig = malloc (sizeof (*sig)); + if (!sig) + return GPGME_Out_Of_Core; + + sig->next = NULL; + switch (*args) + { + case 'S': + sig->type = GPGME_SIG_MODE_NORMAL; + break; + + case 'D': + sig->type = GPGME_SIG_MODE_DETACH; + break; + + case 'C': + sig->type = GPGME_SIG_MODE_CLEAR; + break; + + default: + /* The backend engine is not behaving. */ + free (sig); + return GPGME_General_Error; + } + + args++; + if (*args != ' ') + { + free (sig); + return GPGME_General_Error; + } + + errno = 0; + sig->pubkey_algo = strtol (args, &tail, 0); + if (errno || args == tail || *tail != ' ') + { + /* The crypto backend does not behave. */ + free (sig); + return GPGME_General_Error; + } + args = tail; + + sig->hash_algo = strtol (args, &tail, 0); + if (errno || args == tail || *tail != ' ') + { + /* The crypto backend does not behave. */ + free (sig); + return GPGME_General_Error; + } + args = tail; + + sig->class = strtol (args, &tail, 0); + if (errno || args == tail || *tail != ' ') + { + /* The crypto backend does not behave. */ + free (sig); + return GPGME_General_Error; + } + args = tail; + + sig->created = strtol (args, &tail, 0); + if (errno || args == tail || *tail != ' ') + { + /* The crypto backend does not behave. */ + free (sig); + return GPGME_General_Error; + } + args = tail; + while (*args == ' ') + args++; + + if (!*args) + { + /* The crypto backend does not behave. */ + free (sig); + return GPGME_General_Error; + } + + tail = strchr (args, ' '); + if (tail) + *tail = '\0'; + + sig->fpr = strdup (args); + if (!sig->fpr) + { + free (sig); + return GPGME_Out_Of_Core; + } + *sigp = sig; + return 0; +} + + +GpgmeError +_gpgme_sign_status_handler (void *priv, GpgmeStatusCode code, char *args) +{ + GpgmeCtx ctx = (GpgmeCtx) priv; + GpgmeError err; + op_data_t opd; + + err = _gpgme_passphrase_status_handler (priv, code, args); + if (err) + return err; + + err = _gpgme_op_data_lookup (ctx, OPDATA_SIGN, (void **) &opd, -1, NULL); if (err) return err; switch (code) { - case GPGME_STATUS_EOF: - err = _gpgme_op_data_lookup (ctx, OPDATA_SIGN, (void **) &result, - -1, NULL); - if (!err) - { - if (result && result->okay) - { - append_xml_siginfo (&result->xmlinfo, NULL); - _gpgme_set_op_info (ctx, result->xmlinfo); - result->xmlinfo = NULL; - } - else if (!result || !result->okay) - /* FIXME: choose a better error code? */ - err = GPGME_No_Data; - } + case GPGME_STATUS_SIG_CREATED: + err = parse_sig_created (args, opd->last_sig_p); + if (err) + return err; + + opd->last_sig_p = &(*opd->last_sig_p)->next; break; - case GPGME_STATUS_SIG_CREATED: - /* FIXME: We have no error return for multiple signatures. */ - err = _gpgme_op_data_lookup (ctx, OPDATA_SIGN, (void **) &result, - sizeof (*result), release_sign_result); - append_xml_siginfo (&result->xmlinfo, args); - result->okay = 1; + case GPGME_STATUS_INV_RECP: + err = _gpgme_parse_inv_userid (args, opd->last_signer_p); + if (err) + return err; + + opd->last_signer_p = &(*opd->last_signer_p)->next; + break; + + case GPGME_STATUS_EOF: + if (opd->result.invalid_signers) + return GPGME_Invalid_UserID; break; default: @@ -180,10 +226,26 @@ _gpgme_sign_status_handler (GpgmeCtx ctx, GpgmeStatusCode code, char *args) return err; } + +GpgmeError +_gpgme_op_sign_init_result (GpgmeCtx ctx) +{ + GpgmeError err; + op_data_t opd; + + err = _gpgme_op_data_lookup (ctx, OPDATA_SIGN, (void **) &opd, + sizeof (*opd), release_op_data); + if (err) + return err; + opd->last_signer_p = &opd->result.invalid_signers; + opd->last_sig_p = &opd->result.signatures; + return 0; +} + + static GpgmeError -_gpgme_op_sign_start (GpgmeCtx ctx, int synchronous, - GpgmeData plain, GpgmeData sig, - GpgmeSigMode mode) +sign_start (GpgmeCtx ctx, int synchronous, GpgmeData plain, GpgmeData sig, + GpgmeSigMode mode) { GpgmeError err; @@ -191,6 +253,10 @@ _gpgme_op_sign_start (GpgmeCtx ctx, int synchronous, if (err) return err; + err = _gpgme_op_sign_init_result (ctx); + if (err) + return err; + if (mode != GPGME_SIG_MODE_NORMAL && mode != GPGME_SIG_MODE_DETACH && mode != GPGME_SIG_MODE_CLEAR) return GPGME_Invalid_Value; @@ -217,38 +283,21 @@ _gpgme_op_sign_start (GpgmeCtx ctx, int synchronous, ctx /* FIXME */); } + +/* Sign the plaintext PLAIN and store the signature in SIG. */ GpgmeError -gpgme_op_sign_start (GpgmeCtx ctx, GpgmeData in, GpgmeData out, +gpgme_op_sign_start (GpgmeCtx ctx, GpgmeData plain, GpgmeData sig, GpgmeSigMode mode) { - return _gpgme_op_sign_start (ctx, 0, in, out, mode); + return sign_start (ctx, 0, plain, sig, mode); } -/** - * gpgme_op_sign: - * @ctx: The context - * @in: Data to be signed - * @out: Detached signature - * @mode: Signature creation mode - * - * Create a detached signature for @in and write it to @out. - * The data will be signed using either the default key or the ones - * defined through @ctx. - * The defined modes for signature create are: - * - * GPGME_SIG_MODE_NORMAL (or 0) - * GPGME_SIG_MODE_DETACH - * GPGME_SIG_MODE_CLEAR - * - * Note that the settings done by gpgme_set_armor() and gpgme_set_textmode() - * are ignore for @mode GPGME_SIG_MODE_CLEAR. - * - * Return value: 0 on success or an error code. - **/ + +/* Sign the plaintext PLAIN and store the signature in SIG. */ GpgmeError -gpgme_op_sign (GpgmeCtx ctx, GpgmeData in, GpgmeData out, GpgmeSigMode mode) +gpgme_op_sign (GpgmeCtx ctx, GpgmeData plain, GpgmeData sig, GpgmeSigMode mode) { - GpgmeError err = _gpgme_op_sign_start (ctx, 1, in, out, mode); + GpgmeError err = sign_start (ctx, 1, plain, sig, mode); if (!err) err = _gpgme_wait_one (ctx); return err;