]> git.saurik.com Git - apple/security.git/blobdiff - SecurityTests/cspxutils/sshKey/sshKey.cpp
Security-57740.51.3.tar.gz
[apple/security.git] / SecurityTests / cspxutils / sshKey / sshKey.cpp
diff --git a/SecurityTests/cspxutils/sshKey/sshKey.cpp b/SecurityTests/cspxutils/sshKey/sshKey.cpp
deleted file mode 100644 (file)
index 6b18122..0000000
+++ /dev/null
@@ -1,1461 +0,0 @@
-/*
- * sshKey.cpp - Standalone SSH key parser and converter. Uses libcrypto for 
- *              representing and storing RSA and DSA keys and for 
- *              writing and reading BIGNUMS to/from memory.
- */
-#include <stdlib.h>
-#include <strings.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <CommonCrypto/CommonDigest.h>
-#include <CommonCrypto/CommonCryptor.h>
-#include <security_cdsa_utils/cuFileIo.h>
-#include <security_cdsa_utils/cuEnc64.h>
-#include <openssl/rsa.h>
-#include <openssl/dsa.h>
-#include <CoreFoundation/CoreFoundation.h>
-#include <security_utilities/devrandom.h>
-#include <ctype.h>
-
-#define dprintf(s...)          printf(s)
-
-static void usage(char **argv)
-{
-       printf("usage: %s [options]\n", argv[0]);
-       printf("Options:\n");
-       printf("  -i inFile\n");
-       printf("  -o outFile\n");
-       printf("  -v             -- private key input; default is public\n");
-       printf("  -V             -- private key output; default is public\n");
-       printf("  -d             -- DSA; default is RSA\n");
-       printf("  -r             -- parse & print inFile\n");
-       printf("  -f ssh1|ssh2   -- input format; default = ssh2\n");
-       printf("  -F ssh1|ssh2   -- output format; default = ssh2\n");
-       printf("  -p password\n");
-       printf("  -P             -- no password; private keys in the clear\n");
-       printf("  -c comment\n");
-       exit(1);
-}
-
-static const char *authfile_id_string = "SSH PRIVATE KEY FILE FORMAT 1.1\n";
-
-/* from openssh cipher.h */
-#define SSH_CIPHER_NONE                0       /* no encryption */
-#define SSH_CIPHER_IDEA                1       /* IDEA CFB */
-#define SSH_CIPHER_DES         2       /* DES CBC */
-#define SSH_CIPHER_3DES                3       /* 3DES CBC */
-#define SSH_CIPHER_BROKEN_TSS  4       /* TRI's Simple Stream encryption CBC */
-#define SSH_CIPHER_BROKEN_RC4  5       /* Alleged RC4 */
-#define SSH_CIPHER_BLOWFISH    6
-#define SSH_CIPHER_RESERVED    7
-
-#define SSH2_RSA_HEADER                "ssh-rsa"
-#define SSH2_DSA_HEADER                "ssh-dss"
-
-#pragma mark --- commmon code --- 
-
-static uint32_t readUint32(
-       const unsigned char *&cp,               // IN/OUT
-       unsigned &len)                                  // IN/OUT 
-{
-       uint32_t r = 0;
-       
-       for(unsigned dex=0; dex<sizeof(uint32_t); dex++) {
-               r <<= 8;
-               r |= *cp++;
-       }
-       len -= 4;
-       return r;
-}
-
-static uint16_t readUint16(
-       const unsigned char *&cp,               // IN/OUT
-       unsigned &len)                                  // IN/OUT 
-{
-       uint16_t r = *cp++;
-       r <<= 8;
-       r |= *cp++;
-       len -= 2;
-       return r;
-}
-
-static void appendUint32(
-       CFMutableDataRef cfOut,
-       uint32_t ui)
-{
-       UInt8 buf[sizeof(uint32_t)];
-       
-       for(int dex=(sizeof(uint32_t) - 1); dex>=0; dex--) {
-               buf[dex] = ui & 0xff;
-               ui >>= 8;
-       }
-       CFDataAppendBytes(cfOut, buf, sizeof(uint32_t));
-}
-
-static void appendUint16(
-       CFMutableDataRef cfOut,
-       uint16_t ui)
-{
-       UInt8 buf[sizeof(uint16_t)];
-       
-       buf[1] = ui & 0xff;
-       ui >>= 8;
-       buf[0] = ui;
-       CFDataAppendBytes(cfOut, buf, sizeof(uint16_t));
-}
-
-/* parse text as decimal, return BIGNUM */
-static BIGNUM *parseDecimalBn(
-       const unsigned char *cp,
-       unsigned len)
-{
-       for(unsigned dex=0; dex<len; dex++) {
-               char c = *cp;
-               if((c < '0') || (c > '9')) {
-                       return NULL;
-               }
-       }
-       char *str = (char *)malloc(len + 1);
-       memmove(str, cp, len);
-       str[len] = '\0';
-       BIGNUM *bn = NULL;
-       BN_dec2bn(&bn, str);
-       free(str);
-       return bn;
-}
-
-/* Read BIGNUM, OpenSSH-1 version */
-static BIGNUM *readBigNum(
-       const unsigned char *&cp,       // IN/OUT
-       unsigned &remLen)                       // IN/OUT
-{
-       if(remLen < sizeof(uint16_t)) {
-               dprintf("readBigNum: short record(1)\n");
-               return NULL;
-       }
-       uint16_t numBits = readUint16(cp, remLen);
-       unsigned bytes = (numBits + 7) / 8;
-       if(remLen < bytes) {
-               dprintf("readBigNum: short record(2)\n");
-               return NULL;
-       }
-       BIGNUM *bn = BN_bin2bn(cp, bytes, NULL);
-       if(bn == NULL) {
-               dprintf("readBigNum: BN_bin2bn error\n");
-               return NULL;
-       }
-       cp += bytes;
-       remLen -= bytes;
-       return bn;
-}
-       
-/* Write BIGNUM, OpenSSH-1 version */
-static int appendBigNum(
-       CFMutableDataRef cfOut, 
-       const BIGNUM *bn)
-{
-       /* 16 bits of numbits */
-       unsigned numBits = BN_num_bits(bn);
-       appendUint16(cfOut, numBits);
-       
-       /* serialize the bytes */
-       int numBytes = (numBits + 7) / 8;
-       unsigned char outBytes[numBytes];       // gcc is so cool...
-       int moved = BN_bn2bin(bn, outBytes);
-       if(moved != numBytes) {
-               dprintf("appendBigNum: BN_bn2bin() screwup\n");
-               return -1;
-       }
-       CFDataAppendBytes(cfOut, (UInt8 *)outBytes, numBytes);
-       return 0;
-}
-
-/* read BIGNUM, OpenSSH-2 mpint version */
-static BIGNUM *readBigNum2(
-       const unsigned char *&cp,       // IN/OUT
-       unsigned &remLen)                       // IN/OUT
-{
-       if(remLen < 4) {
-               dprintf("readBigNum2: short record(1)\n");
-               return NULL;
-       }
-       uint32_t bytes = readUint32(cp, remLen);
-       if(remLen < bytes) {
-               dprintf("readBigNum2: short record(2)\n");
-               return NULL;
-       }
-       BIGNUM *bn = BN_bin2bn(cp, bytes, NULL);
-       if(bn == NULL) {
-               dprintf("readBigNum2: BN_bin2bn error\n");
-               return NULL;
-       }
-       cp += bytes;
-       remLen -= bytes;
-       return bn;
-}
-
-/* write BIGNUM, OpenSSH v2 format (with a 4-byte byte count) */
-static int appendBigNum2(
-       CFMutableDataRef cfOut,
-       const BIGNUM *bn)
-{
-       if(bn == NULL) {
-               dprintf("appendBigNum2: NULL bn");
-               return -1;
-       }
-       if (BN_is_zero(bn)) {
-               appendUint32(cfOut, 0);
-               return 0;
-       }
-       if(bn->neg) {
-               dprintf("appendBigNum2: negative numbers not supported\n");
-               return -1;
-       }
-       int numBytes = BN_num_bytes(bn);
-       unsigned char buf[numBytes];
-       int moved = BN_bn2bin(bn, buf);
-       if(moved != numBytes) {
-               dprintf("appendBigNum: BN_bn2bin() screwup\n");
-               return -1;
-       }
-       bool appendZero = false;
-       if(buf[0] & 0x80) {
-               /* prepend leading zero to make it positive */
-               appendZero = true;
-               numBytes++;             // to encode the correct 4-byte length 
-       }
-       appendUint32(cfOut, (uint32_t)numBytes);
-       if(appendZero) {
-               UInt8 z = 0;
-               CFDataAppendBytes(cfOut, &z, 1);
-               numBytes--;             // to append the correct number of bytes
-       }
-       CFDataAppendBytes(cfOut, buf, numBytes);
-       memset(buf, 0, numBytes);
-       return 0;
-}
-
-/* Write BIGNUM, OpenSSH-1 decimal (public key) version */
-static int appendBigNumDec(
-       CFMutableDataRef cfOut, 
-       const BIGNUM *bn)
-{
-       char *buf = BN_bn2dec(bn);
-       if(buf == NULL) {
-               dprintf("appendBigNumDec: BN_bn2dec() error");
-               return -1;
-       }
-       CFDataAppendBytes(cfOut, (const UInt8 *)buf, strlen(buf));
-       OPENSSL_free(buf);
-       return 0;
-}
-
-/* write string, OpenSSH v2 format (with a 4-byte byte count) */
-static void appendString(
-       CFMutableDataRef cfOut,
-       const char *str,
-       unsigned strLen)
-{
-       appendUint32(cfOut, (uint32_t)strLen);
-       CFDataAppendBytes(cfOut, (UInt8 *)str, strLen);
-}
-
-/* skip whitespace */
-static void skipWhite(
-       const unsigned char *&cp,
-       unsigned &bytesLeft)
-{
-       while(bytesLeft != 0) {
-               if(isspace((int)(*cp))) {
-                       cp++;
-                       bytesLeft--;
-               }
-               else {
-                       return;
-               }
-       }
-}
-
-/* find next whitespace or EOF - if EOF, rtn pointer points to one past EOF */
-static const unsigned char *findNextWhite(
-       const unsigned char *cp,
-       unsigned &bytesLeft)
-{
-       while(bytesLeft != 0) {
-               if(isspace((int)(*cp))) {
-                       return cp;
-               }
-               cp++;
-               bytesLeft--;
-       }
-       return cp;
-}
-
-
-/* 
- * Calculate d mod{p-1,q-1}
- * Used when decoding OpenSSH-1 private RSA key.
- */
-static int
-rsa_generate_additional_parameters(RSA *rsa)
-{
-       BIGNUM *aux;
-       BN_CTX *ctx;
-       
-       if((rsa->dmq1 = BN_new()) == NULL) {
-               dprintf("rsa_generate_additional_parameters: BN_new failed");
-               return -1;
-       }
-       if((rsa->dmp1 = BN_new()) == NULL) {
-               dprintf("rsa_generate_additional_parameters: BN_new failed");
-               return -1;
-       }
-       if ((aux = BN_new()) == NULL) {
-               dprintf("rsa_generate_additional_parameters: BN_new failed");
-               return -1;
-       }
-       if ((ctx = BN_CTX_new()) == NULL) {
-               dprintf("rsa_generate_additional_parameters: BN_CTX_new failed");
-               BN_clear_free(aux);
-               return -1;
-       } 
-
-       BN_sub(aux, rsa->q, BN_value_one());
-       BN_mod(rsa->dmq1, rsa->d, aux, ctx);
-
-       BN_sub(aux, rsa->p, BN_value_one());
-       BN_mod(rsa->dmp1, rsa->d, aux, ctx);
-
-       BN_clear_free(aux);
-       BN_CTX_free(ctx);
-       return 0;
-}
-
-#pragma mark --- OpenSSH-1 crypto --- 
-
-static int ssh1DES3Crypt(
-       unsigned char cipher,
-       bool doEncrypt,
-       const unsigned char *inText,
-       unsigned inTextLen,
-       const char *password,           // C string
-       unsigned char *outText,         // data RETURNED here, caller mallocs
-       unsigned *outTextLen)           // RETURNED
-{
-       switch(cipher) {
-               case SSH_CIPHER_3DES:
-                       break;
-               case SSH_CIPHER_NONE:
-                       /* cleartext RSA private key, e.g. host key. */
-                       memmove(outText, inText, inTextLen);
-                       *outTextLen = inTextLen;
-                       return 0;
-               default:
-                       /* who knows how we're going to figure these out */
-                       printf("***Unsupported cipher (%u)\n", cipher);
-                       return -1;
-       }
-       
-       /* key starts with MD5(password) */
-       unsigned char pwdDigest[CC_MD5_DIGEST_LENGTH];
-       CC_MD5(password, strlen(password), pwdDigest);
-       
-       /* three keys from that, like so: */
-       unsigned char k1[kCCKeySizeDES];
-       unsigned char k2[kCCKeySizeDES];
-       unsigned char k3[kCCKeySizeDES];
-       memmove(k1, pwdDigest, kCCKeySizeDES);
-       memmove(k2, pwdDigest + kCCKeySizeDES, kCCKeySizeDES);
-       memmove(k3, pwdDigest, kCCKeySizeDES);
-       
-       CCOperation op1_3;
-       CCOperation op2;
-       if(doEncrypt) {
-               op1_3 = kCCEncrypt;
-               op2   = kCCDecrypt;
-       }
-       else {
-               op1_3 = kCCDecrypt;
-               op2   = kCCEncrypt;
-       }
-       
-       /* the openssh v1 pseudo triple DES. Each DES pass has its own CBC. */
-       size_t moved = 0;
-
-       CCCryptorStatus cstat = CCCrypt(op1_3, kCCAlgorithmDES, 
-               0,                                              // no padding
-               k1, kCCKeySizeDES, 
-               NULL,           // IV
-               inText, inTextLen,
-               outText, inTextLen, &moved);
-       if(cstat) {
-               dprintf("***ssh1DES3Crypt: CCCrypt()(1) returned %u\n", (unsigned)cstat);
-               return -1;
-       }
-       cstat = CCCrypt(op2, kCCAlgorithmDES, 
-               0,                                              // no padding - SSH does that itself 
-               k2, kCCKeySizeDES, 
-               NULL,           // IV
-               outText, moved,
-               outText, inTextLen, &moved);
-       if(cstat) {
-               dprintf("***ssh1DES3Crypt: CCCrypt()(2) returned %u\n", (unsigned)cstat);
-               return -1;
-       }
-       cstat = CCCrypt(op1_3, kCCAlgorithmDES, 
-               0,                                              // no padding - SSH does that itself 
-               k3, kCCKeySizeDES, 
-               NULL,           // IV
-               outText, moved,
-               outText, inTextLen, &moved);
-       if(cstat) {
-               dprintf("***ssh1DES3Crypt: CCCrypt()(3) returned %u\n", (unsigned)cstat);
-               return -1;
-       }
-
-       *outTextLen = moved;    
-       return 0;
-}
-       
-#pragma mark --- OpenSSH-1 decode --- 
-
-/* Decode OpenSSH-1 RSA private key */
-static int decodeSSH1RSAPrivKey(
-       const unsigned char *key,
-       unsigned keyLen,
-       char *password,
-       RSA *rsa,                                               // returned
-       char **comment)                                 // returned
-{
-       const unsigned char *cp = key;          // running pointer
-       unsigned remLen = keyLen;
-       unsigned len = strlen(authfile_id_string);
-       
-       /* length: ID string, NULL, Cipher, 4-byte spare */
-       if(remLen < (len + 6)) {
-               dprintf("decodeSSH1RSAPrivKey: short record(1)\n");
-               return -1;
-       }
-       
-       /* ID string plus a NULL */
-       if(memcmp(authfile_id_string, cp, len)) {
-               dprintf("decodeSSH1RSAPrivKey: bad header\n");
-               return -1;
-       }
-       cp += (len + 1);
-       remLen -= (len + 1);
-       
-       /* cipher */
-       unsigned char cipherSpec = *cp;
-       switch(cipherSpec) {
-               case SSH_CIPHER_NONE:
-                       if(password != NULL) {
-                               dprintf("decodeSSH1RSAPrivKey: Attempt to decrypt plaintext key\n");
-                               return -1;
-                       }
-                       break;
-               case SSH_CIPHER_3DES:
-                       if(password == NULL) {
-                               dprintf("decodeSSH1RSAPrivKey: Encrypted key with no decryptKey\n");
-                               return -1;
-                       }
-                       break;
-               default:
-                       /* I hope we don't see any other values here */
-                       dprintf("decodeOpenSSHv1PrivKey: unknown cipherSpec (%u)\n", cipherSpec);
-                               return -1;
-       }
-       
-       /* skip cipher, spares */
-       cp += 5;
-       remLen -= 5;
-       
-       /*
-        * Clear text public key:
-        * uint32 bits
-        * bignum n
-        * bignum e
-        */
-       if(remLen < sizeof(uint32_t)) {
-               dprintf("decodeSSH1RSAPrivKey: bad len(1)\n");
-               return -1;
-       }
-       /* skip over keybits */
-       readUint32(cp, remLen);
-       rsa->n = readBigNum(cp, remLen);
-       if(rsa->n == NULL) {
-               dprintf("decodeSSH1RSAPrivKey: error decoding n\n");
-               return -1;
-       }
-       rsa->e = readBigNum(cp, remLen);
-       if(rsa->e == NULL) {
-               dprintf("decodeSSH1RSAPrivKey: error decoding e\n");
-               return -1;
-       }
-       
-       /* comment string: 4-byte length and the string w/o NULL */
-       if(remLen < sizeof(uint32_t)) {
-               dprintf("decodeSSH1RSAPrivKey: bad len(2)\n");
-               return -1;
-       }
-       uint32_t commentLen = readUint32(cp, remLen);
-       if(commentLen > remLen) {
-               dprintf("decodeSSH1RSAPrivKey: bad len(3)\n");
-               return -1;
-       }
-       *comment = (char *)malloc(commentLen + 1);
-       memmove(*comment, cp, commentLen);
-       (*comment)[commentLen] = '\0';
-       cp += commentLen;
-       remLen -= commentLen;
-       
-       /* everything that remains is ciphertext */
-       unsigned char ptext[remLen];
-       unsigned ptextLen = 0;
-       if(ssh1DES3Crypt(cipherSpec, false, cp, remLen, password, ptext, &ptextLen)) {
-               dprintf("decodeSSH1RSAPrivKey: decrypt error\n");
-               return -1;
-       }
-       /* subsequent errors to errOut: */
-       
-       int ourRtn = 0;
-       
-       /* plaintext contents:
-       
-       [0-1]           -- random bytes
-       [2-3]           -- copy of [01] for passphrase validity checking
-       buffer_put_bignum(d)
-       buffer_put_bignum(iqmp)
-       buffer_put_bignum(q)
-       buffer_put_bignum(p)
-       pad to block size
-       */
-       cp = ptext;
-       remLen = ptextLen;
-       if(remLen < 4) {
-               dprintf("decodeSSH1RSAPrivKey: bad len(4)\n");
-               ourRtn = -1;
-               goto errOut;
-       }
-       if((cp[0] != cp[2]) || (cp[1] != cp[3])) {
-               /* decrypt fail */
-               dprintf("decodeSSH1RSAPrivKey: check byte error\n");
-               ourRtn = -1;
-               goto errOut;
-       }
-       cp += 4;
-       remLen -= 4;
-       
-       /* remainder comprises private portion of RSA key */
-       rsa->d = readBigNum(cp, remLen);
-       if(rsa->d == NULL) {
-               dprintf("decodeSSH1RSAPrivKey: error decoding d\n");
-               return -1;
-       }
-       rsa->iqmp = readBigNum(cp, remLen);
-       if(rsa->iqmp == NULL) {
-               dprintf("decodeSSH1RSAPrivKey: error decoding iqmp\n");
-               return -1;
-       }
-       rsa->q = readBigNum(cp, remLen);
-       if(rsa->q == NULL) {
-               dprintf("decodeSSH1RSAPrivKey: error decoding q\n");
-               return -1;
-       }
-       rsa->p = readBigNum(cp, remLen);
-       if(rsa->p == NULL) {
-               dprintf("decodeSSH1RSAPrivKey: error decoding p\n");
-               return -1;
-       }
-
-       /* calculate d mod{p-1,q-1} */
-       ourRtn = rsa_generate_additional_parameters(rsa);
-
-errOut:
-       memset(ptext, 0, ptextLen);
-       return ourRtn; 
-}
-
-/* Decode OpenSSH-1 RSA public key */
-static int decodeSSH1RSAPubKey(
-       const unsigned char *key,
-       unsigned keyLen,
-       RSA *rsa,                                               // returned
-       char **comment)                                 // returned
-{
-       const unsigned char *cp = key;          // running pointer
-       unsigned remLen = keyLen;
-       
-       *comment = NULL;
-       skipWhite(cp, remLen);
-       
-       /* 
-        * cp points to start of size_in_bits in ASCII decimal' we really don't care about 
-        * this field. Find next space.
-        */
-       cp = findNextWhite(cp, remLen);
-       if(remLen == 0) {
-               dprintf("decodeSSH1RSAPubKey: short key (1)\n");
-               return -1;
-       }
-       skipWhite(cp, remLen);
-       if(remLen == 0) {
-               dprintf("decodeSSH1RSAPubKey: short key (2)\n");
-               return -1;
-       }
-       
-       /*
-        * cp points to start of e
-        */
-       const unsigned char *ep = findNextWhite(cp, remLen);
-       if(remLen == 0) {
-               dprintf("decodeSSH1RSAPubKey: short key (3)\n");
-               return -1;
-       }
-       unsigned len = ep - cp;
-       rsa->e = parseDecimalBn(cp, len);
-       if(rsa->e == NULL) {
-               return -1;
-       }
-       cp += len;
-       remLen -= len;
-       
-       skipWhite(cp, remLen);
-       if(remLen == 0) {
-               dprintf("decodeSSH1RSAPubKey: short key (4)\n");
-               return -1;
-       }
-       
-       /* cp points to start of n */
-       ep = findNextWhite(cp, remLen);
-       len = ep - cp;
-       rsa->n = parseDecimalBn(cp, len);
-       if(rsa->n == NULL) {
-               return -1;
-       }
-       cp += len;
-       remLen -= len;
-       skipWhite(cp, remLen);
-       if(remLen == 0) {
-               /* no comment; we're done */
-               return 0;
-       }
-
-       ep = findNextWhite(cp, remLen);
-       len = ep - cp;
-       if(len == 0) {
-               return 0;
-       }
-       *comment = (char *)malloc(len + 1);
-       memmove(*comment, cp, len);
-       if((*comment)[len - 1] == '\n') {
-               /* normal case closes with a newline, not part of the comment */
-               len--;
-       }
-       (*comment)[len] = '\0';
-       return 0;
-
-}
-
-#pragma mark --- OpenSSH-1 encode --- 
-
-/* Encode OpenSSH-1 RSA private key */
-static int encodeSSH1RSAPrivKey(
-       RSA *rsa, 
-       const char *password, 
-       const char *comment, 
-       unsigned char **outKey,         // mallocd and RETURNED 
-       unsigned *outKeyLen)            // RETURNED
-{
-       CFMutableDataRef cfOut = CFDataCreateMutable(NULL, 0);
-       
-       /* ID string including NULL */
-       CFDataAppendBytes(cfOut, (const UInt8 *)authfile_id_string, strlen(authfile_id_string) + 1);
-       
-       /* one byte cipher */
-       UInt8 cipherSpec = SSH_CIPHER_3DES;
-       CFDataAppendBytes(cfOut, &cipherSpec, 1);
-       
-       /* spares */
-       UInt8 spares[4] = {0};
-       CFDataAppendBytes(cfOut, spares, 4);
-       
-       /*
-        * Clear text public key:
-        * uint32 bits
-        * bignum n
-        * bignum e
-        */
-       uint32_t keybits = RSA_size(rsa) * 8;
-       appendUint32(cfOut, keybits);
-       appendBigNum(cfOut, rsa->n);
-       appendBigNum(cfOut, rsa->e);
-
-       /* comment string: 4-byte length and the string w/o NULL */
-       if(comment) {
-               uint32_t len = strlen(comment);
-               appendUint32(cfOut, len);
-               CFDataAppendBytes(cfOut, (const UInt8 *)comment, len);
-       }
-       
-       /* 
-        * Remainder is encrypted, consisting of
-        *
-        * [0-1]                -- random bytes
-        * [2-3]                -- copy of [01] for passphrase validity checking
-        * buffer_put_bignum(d)
-        * buffer_put_bignum(iqmp)
-        * buffer_put_bignum(q)
-        * buffer_put_bignum(p)
-        * pad to block size
-        */
-       CFMutableDataRef ptext = CFDataCreateMutable(NULL, 0);
-       
-       /* [0..3] check bytes */
-       UInt8 checkBytes[4];
-       DevRandomGenerator rng = DevRandomGenerator();
-       rng.random(checkBytes, 2);
-       checkBytes[2] = checkBytes[0];
-       checkBytes[3] = checkBytes[1];
-       CFDataAppendBytes(ptext, checkBytes, 4);
-       
-       /* d, iqmp, q, p */
-       appendBigNum(ptext, rsa->d);
-       appendBigNum(ptext, rsa->iqmp);
-       appendBigNum(ptext, rsa->q);
-       appendBigNum(ptext, rsa->p);
-       
-       /* encrypt it */
-       unsigned ptextLen = CFDataGetLength(ptext);
-       unsigned padding = 0;
-       unsigned rem = ptextLen & 0x7;
-       if(rem) {
-               padding = 8 - rem;
-       }
-       UInt8 padByte = 0;
-       for(unsigned dex=0; dex<padding; dex++) {
-               CFDataAppendBytes(ptext, &padByte, 1);
-       }
-       ptextLen = CFDataGetLength(ptext);
-       unsigned char ctext[ptextLen];
-       unsigned ctextLen;
-       int ourRtn = ssh1DES3Crypt(SSH_CIPHER_3DES, true, 
-               (unsigned char *)CFDataGetBytePtr(ptext), ptextLen, 
-               password,
-               ctext, &ctextLen);
-       if(ourRtn != 0) {
-               goto errOut;
-       }
-       
-       /* appended encrypted portion */
-       CFDataAppendBytes(cfOut, ctext, ctextLen);
-       *outKeyLen = (unsigned)CFDataGetLength(cfOut);
-       *outKey = (unsigned char *)malloc(*outKeyLen);
-       memmove(*outKey, CFDataGetBytePtr(cfOut), *outKeyLen);
-errOut:
-       CFRelease(cfOut);
-       /* it would be proper to zero out ptext here, but we can't do that to a CFData */
-       CFRelease(ptext);
-       return ourRtn;
-}
-
-/* Encode OpenSSH-1 RSA public key */
-static int encodeSSH1RSAPubKey(
-       RSA *rsa, 
-       const char *comment, 
-       unsigned char **outKey,         // mallocd and RETURNED 
-       unsigned *outKeyLen)            // RETURNED
-{
-       CFMutableDataRef cfOut = CFDataCreateMutable(NULL, 0);
-       
-       /*
-        * Format is
-        * num_bits in decimal
-        * <space>
-        * e, bignum in decimal
-        * <space>
-        * n, bignum in decimal
-        * <space>
-        * optional comment
-        * newline
-        */
-       int ourRtn = 0;
-       unsigned numBits = BN_num_bits(rsa->n);
-       char bitString[20];
-       UInt8 c = ' ';
-
-       snprintf(bitString, sizeof(bitString), "%u ", numBits);
-       CFDataAppendBytes(cfOut, (const UInt8 *)bitString, strlen(bitString));
-       if(appendBigNumDec(cfOut, rsa->e)) {
-               ourRtn = -1;
-               goto errOut;
-       }
-       CFDataAppendBytes(cfOut, &c, 1);
-       if(appendBigNumDec(cfOut, rsa->n)) {
-               ourRtn = -1;
-               goto errOut;
-       }
-       if(comment != NULL) {
-               CFDataAppendBytes(cfOut, &c, 1);
-               CFDataAppendBytes(cfOut, (UInt8 *)comment, strlen(comment));
-       }
-       c = '\n';
-       CFDataAppendBytes(cfOut, &c, 1);
-       *outKeyLen = CFDataGetLength(cfOut);
-       *outKey = (unsigned char *)malloc(*outKeyLen);
-       memmove(*outKey, CFDataGetBytePtr(cfOut), *outKeyLen);
-errOut:
-       CFRelease(cfOut);
-       return ourRtn;
-}
-
-#pragma mark --- OpenSSH-2 public key decode --- 
-
-/* 
- * Decode components from an SSHv2 public key.
- * Also verifies the leading header, e.g. "ssh-rsa".
- * The returned decodedBlob is algorithm-specific.
- */
-static int parseSSH2PubKey(
-       const unsigned char *key,
-       unsigned keyLen,
-       const char *header,                             // SSH2_RSA_HEADER, SSH2_DSA_HEADER
-       unsigned char **decodedBlob,    // mallocd and RETURNED
-       unsigned *decodedBlobLen,               // RETURNED
-       char **comment)                                 // optionally mallocd and RETURNED, NULL terminated
-{
-       unsigned len = strlen(header);
-       const unsigned char *endOfKey = key + keyLen;
-       *decodedBlob = NULL;
-       *comment = NULL;
-       
-       /* ID string plus at least one space */
-       if(keyLen < (len + 1)) {
-               dprintf("parseSSH2PubKey: short record(1)\n");
-               return -1;
-       }
-       
-       if(memcmp(header, key, len)) {
-               dprintf("parseSSH2PubKey: bad header (1)\n");
-               return -1;
-       }
-       key += len;
-       if(*key++ != ' ') {
-               dprintf("parseSSH2PubKey: bad header (2)\n");
-               return -1;
-       }
-       keyLen -= (len + 1);
-
-       /* key points to first whitespace after header */
-       skipWhite(key, keyLen);
-       if(keyLen == 0) {
-               dprintf("parseSSH2PubKey: short key\n");
-               return -1;
-       }
-       
-       /* key is start of base64 blob */
-       const unsigned char *encodedBlob = key;
-       const unsigned char *endBlob = findNextWhite(key, keyLen);
-       unsigned encodedBlobLen = endBlob - encodedBlob;
-       
-       /* decode base 64 */
-       *decodedBlob = cuDec64(encodedBlob, encodedBlobLen, decodedBlobLen);
-       if(*decodedBlob == NULL) {
-               dprintf("parseSSH2PubKey: base64 decode error\n");
-               return -1;
-       }
-       
-       /* skip over the encoded blob and possible whitespace after it */
-       key = endBlob;
-       keyLen = endOfKey - endBlob;
-       skipWhite(key, keyLen);
-       if(keyLen == 0) {
-               /* nothing remains, no comment, no error */
-               return 0;
-       }
-       
-       /* optional comment */
-       *comment = (char *)malloc(keyLen + 1);
-       memmove(*comment, key, keyLen);
-       if((*comment)[keyLen - 1] == '\n') {
-               /* normal case closes with a newline, not part of the comment */
-               keyLen--;
-       }
-       (*comment)[keyLen] = '\0';
-       return 0;
-}
-       
-static int decodeSSH2RSAPubKey(
-       const unsigned char *key,
-       unsigned keyLen,
-       RSA *rsa,                                               // returned
-       char **comment)                                 // returned
-{
-       /* 
-        * Verify header
-        * get base64-decoded blob plus optional comment 
-        */
-       unsigned char *decodedBlob = NULL;
-       unsigned decodedBlobLen = 0;
-       if(parseSSH2PubKey(key, keyLen, SSH2_RSA_HEADER, &decodedBlob, &decodedBlobLen, comment)) {
-               return -1;
-       }
-       /* subsequent errors to errOut: */
-       
-       /*
-        * The inner base64-decoded blob, consisting of
-        * ssh-rsa
-        * e
-        * n
-        */
-       uint32_t decLen;
-       unsigned len;
-       int ourRtn = 0;
-       
-       key = decodedBlob;
-       keyLen = decodedBlobLen;
-       if(keyLen < 12) {
-               /* three length fields at least */
-               dprintf("decodeSSH2RSAPubKey: short record(2)\n");
-               ourRtn = -1;
-               goto errOut;
-       }
-       decLen = readUint32(key, keyLen);
-       len = strlen(SSH2_RSA_HEADER);
-       if(decLen != len) {
-               dprintf("decodeSSH2RSAPubKey: bad header (2)\n");
-               ourRtn = -1;
-               goto errOut;
-       }
-       if(memcmp(SSH2_RSA_HEADER, key, len)) {
-               dprintf("decodeSSH2RSAPubKey: bad header (1)\n");
-               return -1;
-       }
-       key += len;
-       keyLen -= len;
-       
-       rsa->e = readBigNum2(key, keyLen);
-       if(rsa->e == NULL) {
-               ourRtn = -1;
-               goto errOut;
-       }
-       rsa->n = readBigNum2(key, keyLen);
-       if(rsa->n == NULL) {
-               ourRtn = -1;
-               goto errOut;
-       }
-
-errOut:
-       free(decodedBlob);
-       return ourRtn;
-}
-
-static int decodeSSH2DSAPubKey(
-       const unsigned char *key,
-       unsigned keyLen,
-       DSA *dsa,                                               // returned
-       char **comment)                                 // returned
-{
-       /* 
-        * Verify header
-        * get base64-decoded blob plus optional comment 
-        */
-       unsigned char *decodedBlob = NULL;
-       unsigned decodedBlobLen = 0;
-       if(parseSSH2PubKey(key, keyLen, SSH2_DSA_HEADER, &decodedBlob, &decodedBlobLen, comment)) {
-               return -1;
-       }
-       /* subsequent errors to errOut: */
-
-       /*
-        * The inner base64-decoded blob, consisting of
-        * ssh-dss
-        * p
-        * q
-        * g
-        * pub_key
-        */
-       uint32_t decLen;
-       int ourRtn = 0;
-       unsigned len;
-       
-       key = decodedBlob;
-       keyLen = decodedBlobLen;
-       if(keyLen < 20) {
-               /* five length fields at least */
-               dprintf("decodeSSH2DSAPubKey: short record(2)\n");
-               ourRtn = -1;
-               goto errOut;
-       }
-       decLen = readUint32(key, keyLen);
-       len = strlen(SSH2_DSA_HEADER);
-       if(decLen != len) {
-               dprintf("decodeSSH2DSAPubKey: bad header (2)\n");
-               ourRtn = -1;
-               goto errOut;
-       }
-       if(memcmp(SSH2_DSA_HEADER, key, len)) {
-               dprintf("decodeSSH2DSAPubKey: bad header (1)\n");
-               return -1;
-       }
-       key += len;
-       keyLen -= len;
-       
-       dsa->p = readBigNum2(key, keyLen);
-       if(dsa->p == NULL) {
-               ourRtn = -1;
-               goto errOut;
-       }
-       dsa->q = readBigNum2(key, keyLen);
-       if(dsa->q == NULL) {
-               ourRtn = -1;
-               goto errOut;
-       }
-       dsa->g = readBigNum2(key, keyLen);
-       if(dsa->g == NULL) {
-               ourRtn = -1;
-               goto errOut;
-       }
-       dsa->pub_key = readBigNum2(key, keyLen);
-       if(dsa->pub_key == NULL) {
-               ourRtn = -1;
-               goto errOut;
-       }
-
-errOut:
-       free(decodedBlob);
-       return ourRtn;
-}
-
-#pragma mark --- OpenSSH-2 public key encode --- 
-
-static int encodeSSH2RSAPubKey(
-       RSA *rsa, 
-       const char *comment, 
-       unsigned char **outKey,         // mallocd and RETURNED 
-       unsigned *outKeyLen)            // RETURNED
-{
-       unsigned char *b64 = NULL;
-       unsigned b64Len;
-       UInt8 c;
-       
-       /* 
-        * First, the inner base64-encoded blob, consisting of
-        * ssh-rsa
-        * e
-        * n
-        */
-       CFMutableDataRef cfOut = CFDataCreateMutable(NULL, 0);
-       int ourRtn = 0;
-       appendString(cfOut, SSH2_RSA_HEADER, strlen(SSH2_RSA_HEADER));
-       ourRtn = appendBigNum2(cfOut, rsa->e);
-       if(ourRtn) {
-               goto errOut;
-       }
-       ourRtn = appendBigNum2(cfOut, rsa->n);
-       if(ourRtn) {
-               goto errOut;
-       }
-       
-       /* base64 encode that */
-       b64 = cuEnc64((unsigned char *)CFDataGetBytePtr(cfOut), CFDataGetLength(cfOut), &b64Len);
-       
-       /* cuEnc64 added newline and NULL, which we really don't want */
-       b64Len -= 2;
-       
-       /* Now start over, dropping that base64 into a public blob. */
-       CFDataSetLength(cfOut, 0);
-       CFDataAppendBytes(cfOut, (UInt8 *)SSH2_RSA_HEADER, strlen(SSH2_RSA_HEADER));
-       c = ' ';
-       CFDataAppendBytes(cfOut, &c, 1);
-       CFDataAppendBytes(cfOut, b64, b64Len);
-       
-       /* optional comment */
-       if(comment) {
-               CFDataAppendBytes(cfOut, &c, 1);
-               CFDataAppendBytes(cfOut, (UInt8 *)comment, strlen(comment));
-       }
-       
-       /* finish it with a newline */
-       c = '\n';
-       CFDataAppendBytes(cfOut, &c, 1);
-       
-       *outKeyLen = (unsigned)CFDataGetLength(cfOut);
-       *outKey = (unsigned char *)malloc(*outKeyLen);
-       memmove(*outKey, CFDataGetBytePtr(cfOut), *outKeyLen);
-       
-errOut:
-       CFRelease(cfOut);
-       if(b64) {
-               free(b64);
-       }
-       return ourRtn;
-}
-
-static int encodeSSH2DSAPubKey(
-       DSA *dsa, 
-       const char *comment, 
-       unsigned char **outKey,         // mallocd and RETURNED 
-       unsigned *outKeyLen)            // RETURNED
-{
-       unsigned char *b64 = NULL;
-       unsigned b64Len;
-       UInt8 c;
-       
-       /* 
-        * First, the inner base64-encoded blob, consisting of
-        * ssh-dss
-        * p
-        * q
-        * g
-        * pub_key
-        */
-       CFMutableDataRef cfOut = CFDataCreateMutable(NULL, 0);
-       int ourRtn = 0;
-       appendString(cfOut, SSH2_DSA_HEADER, strlen(SSH2_DSA_HEADER));
-       ourRtn = appendBigNum2(cfOut, dsa->p);
-       if(ourRtn) {
-               goto errOut;
-       }
-       ourRtn = appendBigNum2(cfOut, dsa->q);
-       if(ourRtn) {
-               goto errOut;
-       }
-       ourRtn = appendBigNum2(cfOut, dsa->g);
-       if(ourRtn) {
-               goto errOut;
-       }
-       ourRtn = appendBigNum2(cfOut, dsa->pub_key);
-       if(ourRtn) {
-               goto errOut;
-       }
-       
-       /* base64 encode that */
-       b64 = cuEnc64((unsigned char *)CFDataGetBytePtr(cfOut), CFDataGetLength(cfOut), &b64Len);
-       
-       /* cuEnc64 added newline and NULL, which we really don't want */
-       b64Len -= 2;
-       
-       /* Now start over, dropping that base64 into a public blob. */
-       CFDataSetLength(cfOut, 0);
-       CFDataAppendBytes(cfOut, (UInt8 *)SSH2_DSA_HEADER, strlen(SSH2_DSA_HEADER));
-       c = ' ';
-       CFDataAppendBytes(cfOut, &c, 1);
-       CFDataAppendBytes(cfOut, b64, b64Len);
-       
-       /* optional comment */
-       if(comment) {
-               CFDataAppendBytes(cfOut, &c, 1);
-               CFDataAppendBytes(cfOut, (UInt8 *)comment, strlen(comment));
-       }
-       
-       /* finish it with a newline */
-       c = '\n';
-       CFDataAppendBytes(cfOut, &c, 1);
-       
-       *outKeyLen = (unsigned)CFDataGetLength(cfOut);
-       *outKey = (unsigned char *)malloc(*outKeyLen);
-       memmove(*outKey, CFDataGetBytePtr(cfOut), *outKeyLen);
-       
-errOut:
-       CFRelease(cfOut);
-       if(b64) {
-               free(b64);
-       }
-       return ourRtn;
-}
-
-
-#pragma mark --- print RSA/DSA keys --- 
-
-static void printBNLong(
-       BN_ULONG bnl)
-{
-       /* for now assume it's 32 bits */
-       unsigned i = bnl >> 24;
-       printf("%02X ", i);
-       i = (bnl >> 16) & 0xff;
-       printf("%02X ", i);
-       i = (bnl >> 8) & 0xff;
-       printf("%02X ", i);
-       i = bnl & 0xff;
-       printf("%02X ", i);
-}
-
-static void printBN(
-       const char *label,
-       BIGNUM *bn)
-{
-       printf("%s: %d bits: bn->top %d: ", label, BN_num_bits(bn), bn->top);
-       for(int dex=bn->top-1; dex>=0; dex--) {
-               printBNLong(bn->d[dex]);
-       }
-       printf("\n");
-}
-static void printRSA(
-       RSA *rsa)
-{
-       if(rsa->n) {
-               printBN("   n", rsa->n);
-       }
-       if(rsa->e) {
-               printBN("   e", rsa->e);
-       }
-       if(rsa->d) {
-               printBN("   d", rsa->d);
-       }
-       if(rsa->p) {
-               printBN("   p", rsa->p);
-       }
-       if(rsa->q) {
-               printBN("   q", rsa->q);
-       }
-       if(rsa->dmp1) {
-               printBN("dmp1", rsa->dmp1);
-       }
-       if(rsa->dmq1) {
-               printBN("dmq1", rsa->dmq1);
-       }
-       if(rsa->iqmp) {
-               printBN("iqmp", rsa->iqmp);
-       }
-}
-
-/* only public keys here */
-static void printDSA(
-       DSA *dsa)
-{
-       if(dsa->p) {
-               printBN("   p", dsa->p);
-       }
-       if(dsa->q) {
-               printBN("   q", dsa->q);
-       }
-       if(dsa->g) {
-               printBN("   g", dsa->g);
-       }
-       if(dsa->pub_key) {
-               printBN(" pub", dsa->pub_key);
-       }
-}
-
-/* parse format string, returns nonzero on error */
-static int parseFormat(
-       const char *formatStr,
-       bool *isSSH1)
-{
-       if(!strcmp(formatStr, "ssh1")) {
-               *isSSH1 = true;
-               return 0;
-       }
-       else if(!strcmp(formatStr, "ssh2")) {
-               *isSSH1 = false;
-               return 0;
-       }
-       else {
-               return -1;
-       }
-}
-
-#pragma mark --- main --- 
-
-/* parse format string */
-int main(int argc, char **argv)
-{
-       char *inFile = NULL;
-       char *outFile = NULL;
-       bool privKeyIn = false;
-       bool privKeyOut = false;
-       char *password = NULL;
-       char *comment = NULL;
-       bool doPrint = false;
-       bool isDSA = false;
-       bool inputSSH1 = false;
-       bool outputSSH1 = false;
-       bool clearPrivKeys = false;
-       
-       int ourRtn = 0;
-       
-       extern char *optarg;
-       int arg;
-       while ((arg = getopt(argc, argv, "i:o:vVdrf:F:p:Pc:h")) != -1) {
-               switch (arg) {
-                       case 'i':
-                               inFile = optarg;
-                               break;
-                       case 'o':
-                               outFile = optarg;
-                               break;
-                       case 'v':
-                               privKeyIn = true;
-                               break;
-                       case 'V':
-                               privKeyOut = true;
-                               break;
-                       case 'd':
-                               isDSA = true;
-                               break;
-                       case 'r':
-                               doPrint = true;
-                               break;
-                       case 'f':
-                               if(parseFormat(optarg, &inputSSH1)) {
-                                       usage(argv);
-                               }
-                               break;
-                       case 'F':
-                               if(parseFormat(optarg, &outputSSH1)) {
-                                       usage(argv);
-                               }
-                               break;
-                       case 'p':
-                               password = optarg;
-                               break;
-                       case 'P':
-                               clearPrivKeys = true;
-                               break;
-                       case 'c':
-                               comment = optarg;
-                               break;
-                       case 'h':
-                       default:
-                               usage(argv);
-               }
-       }
-       
-       if(inFile == NULL) {
-               printf("***You must specify an input file.\n");
-               usage(argv);
-       }
-       if((privKeyIn && !inputSSH1) || (privKeyOut && !outputSSH1)) {
-               printf("***Private keys in SSH2 format are handled elsewhere - Wrapped OpenSSL.\n");
-               exit(1);
-       }
-       if((privKeyIn || privKeyOut) && (password == NULL) & !clearPrivKeys) {
-               printf("***Private key handling requires a password or the -P option.\n");
-               usage(argv);
-       }
-       unsigned char *inKey = NULL;
-       unsigned inKeyLen = 0;
-       if(readFile(inFile, &inKey, &inKeyLen)) {
-               printf("Error reading %s. Aborting.\n", inFile);
-               exit(1);
-       }
-       
-       RSA *rsa = NULL;
-       DSA *dsa = NULL;
-       
-       /* parse incoming key */
-       if(isDSA) {
-               if(inputSSH1) {
-                       printf("***SSHv1 did not support DSA keys.\n");
-                       exit(1);
-               }
-               /* already verified that this is not SSH2 & priv (Wrapped OpenSSL) */
-               dsa = DSA_new();
-               if(decodeSSH2DSAPubKey(inKey, inKeyLen, dsa, &comment)) {
-                       printf("***Error decoding SSH2 DSA public key.\n");
-                       exit(1);
-               }
-       }
-       else {
-               rsa = RSA_new();
-               if(privKeyIn) {
-                       /* already verified that this is SSH1 (SSH2 is Wrapped OpenSSL) */
-                       if(decodeSSH1RSAPrivKey(inKey, inKeyLen, password, rsa, &comment)) {
-                               printf("***Error decoding SSH1 RSA Private key.\n");
-                               exit(1);
-                       }
-               }
-               else {
-                       if(inputSSH1) {
-                               if(decodeSSH1RSAPubKey(inKey, inKeyLen, rsa, &comment)) {
-                                       printf("***Error decoding SSH1 RSA Public key.\n");
-                                       exit(1);
-                               }
-                       }
-                       else {
-                               if(decodeSSH2RSAPubKey(inKey, inKeyLen, rsa, &comment)) {
-                                       printf("***Error decoding SSH2 RSA Public key.\n");
-                                       exit(1);
-                               }
-                       }
-               }
-       }
-
-       /* optionally display the key */
-       if(doPrint) {
-               if(isDSA) {
-                       printf("DSA key:\n");
-                       printDSA(dsa);
-                       printf("Comment: %s\n", comment);
-               }
-               else {
-                       printf("RSA key:\n");
-                       printRSA(rsa);
-                       printf("Comment: %s\n", comment);
-               }
-       }
-       
-       /* optionally convert to (optionally different) output format */
-       
-       if(outFile) {
-               unsigned char *outKey = NULL;
-               unsigned outKeyLen = 0;
-               
-               if(isDSA) {
-                       if(outputSSH1 || privKeyOut) {
-                               printf("***DSA: Only public SSHv2 keys allowed.\n");
-                               exit(1);
-                       }
-                       if(encodeSSH2DSAPubKey(dsa, comment, &outKey, &outKeyLen)) {
-                               printf("***Error encoding DSA public key.\n");
-                               exit(1);
-                       }
-               }
-               else {
-                       if(privKeyOut) {
-                               /* already verified that this is SSH1 (SSH2 is Wrapped OpenSSL) */
-                               if(encodeSSH1RSAPrivKey(rsa, password, comment, &outKey, &outKeyLen)) {
-                                       printf("***Error encoding RSA private key.\n");
-                                       exit(1);
-                               }
-                       }
-                       else {
-                               if(outputSSH1) {
-                                       if(encodeSSH1RSAPubKey(rsa, comment, &outKey, &outKeyLen)) {
-                                               printf("***Error encoding RSA public key.\n");
-                                               exit(1);
-                                       }
-                               }
-                               else {
-                                       if(encodeSSH2RSAPubKey(rsa, comment, &outKey, &outKeyLen)) {
-                                               printf("***Error encoding RSA public key.\n");
-                                               exit(1);
-                                       }
-                               }
-                       }       /* RSA public */
-               }       /* RSA */
-               
-               if(writeFile(outFile, outKey, outKeyLen)) {
-                       printf("***Error writing to %s.\n", outFile);
-                       ourRtn = -1;
-               }
-               else {
-                       printf("...wrote %u bytes to %s.\n", outKeyLen, outFile);
-               }
-               free(outKey);
-       }
-       else if(!doPrint) {
-               printf("...parsed a key but you didn't ask me to do anything with it.\n");
-       }
-       if(rsa) {
-               RSA_free(rsa);
-       }
-       if(dsa) {
-               DSA_free(dsa);
-       }
-       
-       return 0;
-}