1/*
2 * This program is licenced under the same licence as Ruby.
3 * (See the file 'LICENCE'.)
4 * $Id: ossl_pkcs12.c 32199 2011-06-22 08:41:08Z emboss $
5 */
6#include "ossl.h"
7
8#define WrapPKCS12(klass, obj, p12) do { \
9    if(!(p12)) ossl_raise(rb_eRuntimeError, "PKCS12 wasn't initialized."); \
10    (obj) = Data_Wrap_Struct((klass), 0, PKCS12_free, (p12)); \
11} while (0)
12
13#define GetPKCS12(obj, p12) do { \
14    Data_Get_Struct((obj), PKCS12, (p12)); \
15    if(!(p12)) ossl_raise(rb_eRuntimeError, "PKCS12 wasn't initialized."); \
16} while (0)
17
18#define SafeGetPKCS12(obj, p12) do { \
19    OSSL_Check_Kind((obj), cPKCS12); \
20    GetPKCS12((obj), (p12)); \
21} while (0)
22
23#define ossl_pkcs12_set_key(o,v)      rb_iv_set((o), "@key", (v))
24#define ossl_pkcs12_set_cert(o,v)     rb_iv_set((o), "@certificate", (v))
25#define ossl_pkcs12_set_ca_certs(o,v) rb_iv_set((o), "@ca_certs", (v))
26#define ossl_pkcs12_get_key(o)        rb_iv_get((o), "@key")
27#define ossl_pkcs12_get_cert(o)       rb_iv_get((o), "@certificate")
28#define ossl_pkcs12_get_ca_certs(o)   rb_iv_get((o), "@ca_certs")
29
30/*
31 * Classes
32 */
33VALUE cPKCS12;
34VALUE ePKCS12Error;
35
36/*
37 * Private
38 */
39static VALUE
40ossl_pkcs12_s_allocate(VALUE klass)
41{
42    PKCS12 *p12;
43    VALUE obj;
44
45    if(!(p12 = PKCS12_new())) ossl_raise(ePKCS12Error, NULL);
46    WrapPKCS12(klass, obj, p12);
47
48    return obj;
49}
50
51/*
52 * call-seq:
53 *    PKCS12.create(pass, name, key, cert [, ca, [, key_pbe [, cert_pbe [, key_iter [, mac_iter [, keytype]]]]]])
54 *
55 * === Parameters
56 * * +pass+ - string
57 * * +name+ - A string describing the key.
58 * * +key+ - Any PKey.
59 * * +cert+ - A X509::Certificate.
60 * * * The public_key portion of the certificate must contain a valid public key.
61 * * * The not_before and not_after fields must be filled in.
62 * * +ca+ - An optional array of X509::Certificate's.
63 * * +key_pbe+ - string
64 * * +cert_pbe+ - string
65 * * +key_iter+ - integer
66 * * +mac_iter+ - integer
67 * * +keytype+ - An integer representing an MSIE specific extension.
68 *
69 * Any optional arguments may be supplied as nil to preserve the OpenSSL defaults.
70 *
71 * See the OpenSSL documentation for PKCS12_create().
72 */
73static VALUE
74ossl_pkcs12_s_create(int argc, VALUE *argv, VALUE self)
75{
76    VALUE pass, name, pkey, cert, ca, key_nid, cert_nid, key_iter, mac_iter, keytype;
77    VALUE obj;
78    char *passphrase, *friendlyname;
79    EVP_PKEY *key;
80    X509 *x509;
81    STACK_OF(X509) *x509s;
82    int nkey = 0, ncert = 0, kiter = 0, miter = 0, ktype = 0;
83    PKCS12 *p12;
84
85    rb_scan_args(argc, argv, "46", &pass, &name, &pkey, &cert, &ca, &key_nid, &cert_nid, &key_iter, &mac_iter, &keytype);
86    passphrase = NIL_P(pass) ? NULL : StringValuePtr(pass);
87    friendlyname = NIL_P(name) ? NULL : StringValuePtr(name);
88    key = GetPKeyPtr(pkey);
89    x509 = GetX509CertPtr(cert);
90    x509s = NIL_P(ca) ? NULL : ossl_x509_ary2sk(ca);
91/* TODO: make a VALUE to nid function */
92    if (!NIL_P(key_nid)) {
93        if ((nkey = OBJ_txt2nid(StringValuePtr(key_nid))) == NID_undef)
94            ossl_raise(rb_eArgError, "Unknown PBE algorithm %s", StringValuePtr(key_nid));
95    }
96    if (!NIL_P(cert_nid)) {
97        if ((ncert = OBJ_txt2nid(StringValuePtr(cert_nid))) == NID_undef)
98            ossl_raise(rb_eArgError, "Unknown PBE algorithm %s", StringValuePtr(cert_nid));
99    }
100    if (!NIL_P(key_iter))
101        kiter = NUM2INT(key_iter);
102    if (!NIL_P(mac_iter))
103        miter = NUM2INT(mac_iter);
104    if (!NIL_P(keytype))
105        ktype = NUM2INT(keytype);
106
107    p12 = PKCS12_create(passphrase, friendlyname, key, x509, x509s,
108                        nkey, ncert, kiter, miter, ktype);
109    sk_X509_pop_free(x509s, X509_free);
110    if(!p12) ossl_raise(ePKCS12Error, NULL);
111    WrapPKCS12(cPKCS12, obj, p12);
112
113    ossl_pkcs12_set_key(obj, pkey);
114    ossl_pkcs12_set_cert(obj, cert);
115    ossl_pkcs12_set_ca_certs(obj, ca);
116
117    return obj;
118}
119
120/*
121 * call-seq:
122 *    PKCS12.new -> pkcs12
123 *    PKCS12.new(str) -> pkcs12
124 *    PKCS12.new(str, pass) -> pkcs12
125 *
126 * === Parameters
127 * * +str+ - Must be a DER encoded PKCS12 string.
128 * * +pass+ - string
129 */
130static VALUE
131ossl_pkcs12_initialize(int argc, VALUE *argv, VALUE self)
132{
133    BIO *in;
134    VALUE arg, pass, pkey, cert, ca;
135    char *passphrase;
136    EVP_PKEY *key;
137    X509 *x509;
138    STACK_OF(X509) *x509s = NULL;
139    int st = 0;
140    PKCS12 *pkcs = DATA_PTR(self);
141
142    if(rb_scan_args(argc, argv, "02", &arg, &pass) == 0) return self;
143    passphrase = NIL_P(pass) ? NULL : StringValuePtr(pass);
144    in = ossl_obj2bio(arg);
145    d2i_PKCS12_bio(in, &pkcs);
146    DATA_PTR(self) = pkcs;
147    BIO_free(in);
148
149    pkey = cert = ca = Qnil;
150    if(!PKCS12_parse(pkcs, passphrase, &key, &x509, &x509s))
151	ossl_raise(ePKCS12Error, "PKCS12_parse");
152    pkey = rb_protect((VALUE(*)_((VALUE)))ossl_pkey_new, (VALUE)key,
153		      &st); /* NO DUP */
154    if(st) goto err;
155    cert = rb_protect((VALUE(*)_((VALUE)))ossl_x509_new, (VALUE)x509, &st);
156    if(st) goto err;
157    if(x509s){
158	ca =
159	    rb_protect((VALUE(*)_((VALUE)))ossl_x509_sk2ary, (VALUE)x509s, &st);
160	if(st) goto err;
161    }
162
163  err:
164    X509_free(x509);
165    sk_X509_pop_free(x509s, X509_free);
166    ossl_pkcs12_set_key(self, pkey);
167    ossl_pkcs12_set_cert(self, cert);
168    ossl_pkcs12_set_ca_certs(self, ca);
169    if(st) rb_jump_tag(st);
170
171    return self;
172}
173
174static VALUE
175ossl_pkcs12_to_der(VALUE self)
176{
177    PKCS12 *p12;
178    VALUE str;
179    long len;
180    unsigned char *p;
181
182    GetPKCS12(self, p12);
183    if((len = i2d_PKCS12(p12, NULL)) <= 0)
184	ossl_raise(ePKCS12Error, NULL);
185    str = rb_str_new(0, len);
186    p = (unsigned char *)RSTRING_PTR(str);
187    if(i2d_PKCS12(p12, &p) <= 0)
188	ossl_raise(ePKCS12Error, NULL);
189    ossl_str_adjust(str, p);
190
191    return str;
192}
193
194void
195Init_ossl_pkcs12()
196{
197    /*
198     * Defines a file format commonly used to store private keys with
199     * accompanying public key certificates, protected with a password-based
200     * symmetric key.
201     */
202    cPKCS12 = rb_define_class_under(mOSSL, "PKCS12", rb_cObject);
203    ePKCS12Error = rb_define_class_under(cPKCS12, "PKCS12Error", eOSSLError);
204    rb_define_singleton_method(cPKCS12, "create", ossl_pkcs12_s_create, -1);
205
206    rb_define_alloc_func(cPKCS12, ossl_pkcs12_s_allocate);
207    rb_attr(cPKCS12, rb_intern("key"), 1, 0, Qfalse);
208    rb_attr(cPKCS12, rb_intern("certificate"), 1, 0, Qfalse);
209    rb_attr(cPKCS12, rb_intern("ca_certs"), 1, 0, Qfalse);
210    rb_define_method(cPKCS12, "initialize", ossl_pkcs12_initialize, -1);
211    rb_define_method(cPKCS12, "to_der", ossl_pkcs12_to_der, 0);
212}
213