mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-25 03:05:34 +00:00
merged
This commit is contained in:
commit
5f7b9953fd
@ -22,6 +22,7 @@
|
||||
* Contributor(s):
|
||||
* Dan Mills <thunder@mozilla.com> (original author)
|
||||
* Honza Bambas <honzab@allpeers.com>
|
||||
* Justin Dolske <dolske@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
@ -48,46 +49,135 @@ interface IWeaveCrypto : nsISupports
|
||||
*/
|
||||
|
||||
const unsigned long DES_EDE3_CBC = 156;
|
||||
|
||||
// Unsupported now...
|
||||
const unsigned long AES_128_ECB = 183;
|
||||
const unsigned long AES_128_CBC = 184;
|
||||
const unsigned long AES_192_ECB = 185;
|
||||
const unsigned long AES_192_CBC = 186;
|
||||
const unsigned long AES_256_ECB = 187;
|
||||
const unsigned long AES_256_CBC = 188;
|
||||
const unsigned long AES_128_CBC = 184;
|
||||
const unsigned long AES_192_CBC = 186;
|
||||
const unsigned long AES_256_CBC = 188;
|
||||
|
||||
/**
|
||||
* One of the above constants. Assigning differnt value
|
||||
* will fail cause the encrypt method fail.
|
||||
* Default value is DES_EDE3_CBC.
|
||||
* One of the above constants. Used as the mechanism for encrypting bulk
|
||||
* data and wrapping keys.
|
||||
*
|
||||
* Default is AES_256_CBC.
|
||||
*/
|
||||
attribute unsigned long algorithm;
|
||||
|
||||
/**
|
||||
* Encrypt data using a passphrase
|
||||
* See algorithm attribute, it determines which mechanisms will be used
|
||||
* The size of the RSA key to create with generateKeypair().
|
||||
*
|
||||
* @param pass
|
||||
* Passphrase to encrypt with
|
||||
* @param iter
|
||||
* Number of iterations for key strengthening
|
||||
* This parameter is currently ignored and is always 1
|
||||
* @param clearText
|
||||
* Data to be encrypted
|
||||
* @returns Encrypted data, base64 encoded
|
||||
* Default is 2048.
|
||||
*/
|
||||
ACString encrypt(in ACString pass, in ACString clearText);
|
||||
attribute unsigned long keypairBits;
|
||||
|
||||
/**
|
||||
* Decrypt data using a passphrase
|
||||
* Encrypt data using a symmetric key.
|
||||
* The algorithm attribute specifies how the encryption is performed.
|
||||
*
|
||||
* @param pass
|
||||
* Passphrase to decrypt with
|
||||
* @param cipherText
|
||||
* Base64 encoded data to be decrypted
|
||||
* @returns Decrypted data
|
||||
* @param clearText
|
||||
* The data to be encrypted (not base64 encoded).
|
||||
* @param symmetricKey
|
||||
* A base64-encoded symmetric key (eg, one from generateRandomKey).
|
||||
* @param iv
|
||||
* A base64-encoded initialization vector
|
||||
* @returns Encrypted data, base64 encoded
|
||||
*/
|
||||
ACString decrypt(in ACString pass, in ACString cipherText);
|
||||
ACString encrypt(in ACString clearText,
|
||||
in ACString symmetricKey, in ACString iv);
|
||||
|
||||
/**
|
||||
* Encrypt data using a symmetric key.
|
||||
* The algorithm attribute specifies how the encryption is performed.
|
||||
*
|
||||
* @param cipherText
|
||||
* The base64-encoded data to be decrypted
|
||||
* @param symmetricKey
|
||||
* A base64-encoded symmetric key (eg, one from unwrapSymmetricKey)
|
||||
* @param iv
|
||||
* A base64-encoded initialization vector
|
||||
* @returns Decrypted data (not base64-encoded)
|
||||
*/
|
||||
ACString decrypt(in ACString cipherText,
|
||||
in ACString symmetricKey, in ACString iv);
|
||||
|
||||
/**
|
||||
* Generate a RSA public/private keypair.
|
||||
*
|
||||
* @param aPassphrase
|
||||
* User's passphrase. Used with PKCS#5 to generate a symmetric key
|
||||
* for wrapping the private key.
|
||||
* @param aSalt
|
||||
* Salt for the user's passphrase.
|
||||
* @param aIV
|
||||
* Random IV, used when wrapping the private key.
|
||||
* @param aEncodedPublicKey
|
||||
* The public key, base-64 encoded.
|
||||
* @param aWrappedPrivateKey
|
||||
* The public key, encrypted with the user's passphrase, and base-64 encoded.
|
||||
*/
|
||||
void generateKeypair(in ACString aPassphrase, in ACString aSalt, in ACString aIV,
|
||||
out ACString aEncodedPublicKey, out ACString aWrappedPrivateKey);
|
||||
|
||||
/*
|
||||
* Generate a random symmetric key.
|
||||
*
|
||||
* @returns The random key, base64 encoded
|
||||
*/
|
||||
ACString generateRandomKey();
|
||||
|
||||
/*
|
||||
* Generate a random IV.
|
||||
*
|
||||
* The IV will be sized for the algorithm specified in the algorithm
|
||||
* attribute of IWeaveCrypto.
|
||||
*
|
||||
* @returns The random IV, base64 encoded
|
||||
*/
|
||||
ACString generateRandomIV();
|
||||
|
||||
/*
|
||||
* Generate random data.
|
||||
*
|
||||
* @param aByteCount
|
||||
* The number of bytes of random data to generate.
|
||||
* @returns The random bytes, base64-encoded
|
||||
*/
|
||||
ACString generateRandomBytes(in unsigned long aByteCount);
|
||||
|
||||
|
||||
/**
|
||||
* Encrypts a symmetric key with a user's public key.
|
||||
*
|
||||
* @param aSymmetricKey
|
||||
* The base64 encoded string holding a symmetric key.
|
||||
* @param aEncodedPublicKey
|
||||
* The base64 encoded string holding a public key.
|
||||
* @returns The wrapped symmetric key, base64 encoded
|
||||
*
|
||||
* For RSA, the unencoded public key is a PKCS#1 object.
|
||||
*/
|
||||
ACString wrapSymmetricKey(in ACString aSymmetricKey,
|
||||
in ACString aEncodedPublicKey);
|
||||
|
||||
/**
|
||||
* Decrypts a symmetric key with a user's private key.
|
||||
*
|
||||
* @param aWrappedSymmetricKey
|
||||
* The base64 encoded string holding an encrypted symmetric key.
|
||||
* @param aWrappedPrivateKey
|
||||
* The base64 encoded string holdering an encrypted private key.
|
||||
* @param aPassphrase
|
||||
* The passphrase to decrypt the private key.
|
||||
* @param aSalt
|
||||
* The salt for the passphrase.
|
||||
* @param aIV
|
||||
* The random IV used when unwrapping the private key.
|
||||
* @returns The unwrapped symmetric key, base64 encoded
|
||||
*
|
||||
* For RSA, the unencoded, decrypted key is a PKCS#1 object.
|
||||
*/
|
||||
ACString unwrapSymmetricKey(in ACString aWrappedSymmetricKey,
|
||||
in ACString aWrappedPrivateKey,
|
||||
in ACString aPassphrase,
|
||||
in ACString aSalt,
|
||||
in ACString aIV);
|
||||
};
|
||||
|
||||
|
@ -22,6 +22,7 @@
|
||||
* Contributor(s):
|
||||
* Dan Mills <thunder@mozilla.com> (original author)
|
||||
* Honza Bambas <honzab@allpeers.com>
|
||||
* Justin Dolske <dolske@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
@ -43,12 +44,17 @@
|
||||
#include "nsAutoPtr.h"
|
||||
#include "plbase64.h"
|
||||
#include "secerr.h"
|
||||
#include "secpkcs7.h"
|
||||
|
||||
#include "pk11func.h"
|
||||
#include "keyhi.h"
|
||||
#include "nss.h"
|
||||
|
||||
|
||||
NS_IMPL_ISUPPORTS1(WeaveCrypto, IWeaveCrypto)
|
||||
|
||||
WeaveCrypto::WeaveCrypto()
|
||||
: mAlgorithm(SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC)
|
||||
WeaveCrypto::WeaveCrypto() :
|
||||
mAlgorithm(SEC_OID_AES_256_CBC),
|
||||
mKeypairBits(2048)
|
||||
{
|
||||
}
|
||||
|
||||
@ -56,76 +62,67 @@ WeaveCrypto::~WeaveCrypto()
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
WeaveCrypto::EncodeBase64(const nsACString& binary, nsACString& retval)
|
||||
/*
|
||||
* Base 64 encoding and decoding...
|
||||
*/
|
||||
|
||||
void
|
||||
WeaveCrypto::EncodeBase64(const char *aData, PRUint32 aLength, nsACString& retval)
|
||||
{
|
||||
PRUint32 encodedLength = (binary.Length() * 4 + 2) / 3;
|
||||
PRUint32 encodedLength = (aLength + 2) / 3 * 4;
|
||||
char encoded[encodedLength];
|
||||
|
||||
nsAutoArrayPtr<char> encoded;
|
||||
encoded = new char[encodedLength + 2];
|
||||
NS_ENSURE_TRUE(encoded, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
PromiseFlatCString fBinary(binary);
|
||||
PL_Base64Encode(fBinary.get(), fBinary.Length(), encoded);
|
||||
PL_Base64Encode(aData, aLength, encoded);
|
||||
|
||||
retval.Assign(encoded, encodedLength);
|
||||
}
|
||||
|
||||
void
|
||||
WeaveCrypto::EncodeBase64(const nsACString& binary, nsACString& retval)
|
||||
{
|
||||
PromiseFlatCString fBinary(binary);
|
||||
EncodeBase64(fBinary.get(), fBinary.Length(), retval);
|
||||
}
|
||||
|
||||
nsresult
|
||||
WeaveCrypto::DecodeBase64(const nsACString& base64,
|
||||
char *decoded, PRUint32 *decodedSize)
|
||||
{
|
||||
PromiseFlatCString fBase64(base64);
|
||||
|
||||
PRUint32 size = (fBase64.Length() * 3) / 4;
|
||||
// Adjust for padding.
|
||||
if (*(fBase64.get() + fBase64.Length() - 1) == '=')
|
||||
size--;
|
||||
if (*(fBase64.get() + fBase64.Length() - 2) == '=')
|
||||
size--;
|
||||
|
||||
// Just to be paranoid about buffer overflow.
|
||||
if (*decodedSize < size)
|
||||
return NS_ERROR_FAILURE;
|
||||
*decodedSize = size;
|
||||
|
||||
if (!PL_Base64Decode(fBase64.get(), fBase64.Length(), decoded))
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
WeaveCrypto::DecodeBase64(const nsACString& base64, nsACString& retval)
|
||||
{
|
||||
PromiseFlatCString flat(base64);
|
||||
char decoded[base64.Length()];
|
||||
PRUint32 decodedLength = base64.Length();
|
||||
|
||||
PRUint32 decodedLength = (flat.Length() * 3) / 4;
|
||||
|
||||
nsAutoArrayPtr<char> decoded;
|
||||
decoded = new char[decodedLength];
|
||||
NS_ENSURE_TRUE(decoded, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
if (!PL_Base64Decode(flat.get(), flat.Length(), decoded))
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
nsresult rv = DecodeBase64(base64, decoded, &decodedLength);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
retval.Assign(decoded, decodedLength);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
WeaveCrypto::StoreToStringCallback(void *arg, const char *buf, unsigned long len)
|
||||
{
|
||||
nsACString* aText = (nsACString*)arg;
|
||||
aText->Append(buf, len);
|
||||
}
|
||||
|
||||
// static
|
||||
PK11SymKey *
|
||||
WeaveCrypto::GetSymmetricKeyCallback(void *arg, SECAlgorithmID *algid)
|
||||
{
|
||||
SECItem *pwitem = (SECItem *)arg;
|
||||
|
||||
PK11SlotInfo *slot = PK11_GetInternalSlot();
|
||||
if (!slot)
|
||||
return nsnull;
|
||||
|
||||
PK11SymKey *key = PK11_PBEKeyGen(slot, algid, pwitem, PR_FALSE, nsnull);
|
||||
PK11_FreeSlot(slot);
|
||||
|
||||
if (key)
|
||||
PK11_SetSymKeyUserData(key, pwitem, nsnull);
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
// static
|
||||
PRBool
|
||||
WeaveCrypto::DecryptionAllowedCallback(SECAlgorithmID *algid, PK11SymKey *key)
|
||||
{
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// nsIPBECipher
|
||||
|
||||
NS_IMETHODIMP
|
||||
WeaveCrypto::GetAlgorithm(PRUint32 *aAlgorithm)
|
||||
@ -142,78 +139,791 @@ WeaveCrypto::SetAlgorithm(PRUint32 aAlgorithm)
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WeaveCrypto::Encrypt(const nsACString& aPass,
|
||||
const nsACString& aClearText,
|
||||
nsACString& aCipherText)
|
||||
WeaveCrypto::GetKeypairBits(PRUint32 *aBits)
|
||||
{
|
||||
nsresult rv = NS_ERROR_FAILURE;
|
||||
|
||||
SEC_PKCS7ContentInfo *cinfo = SEC_PKCS7CreateEncryptedData(mAlgorithm, 0, nsnull, nsnull);
|
||||
NS_ENSURE_TRUE(cinfo, NS_ERROR_FAILURE);
|
||||
|
||||
SECAlgorithmID* encalgid = SEC_PKCS7GetEncryptionAlgorithm(cinfo);
|
||||
if (encalgid)
|
||||
{
|
||||
PromiseFlatCString fPass(aPass);
|
||||
SECItem pwitem = {siBuffer, (unsigned char*)fPass.get(), fPass.Length()};
|
||||
PK11SymKey *key = GetSymmetricKeyCallback(&pwitem, encalgid);
|
||||
if (key)
|
||||
{
|
||||
nsCString result;
|
||||
SEC_PKCS7EncoderContext *ecx = SEC_PKCS7EncoderStart(
|
||||
cinfo, StoreToStringCallback, &result, key);
|
||||
PK11_FreeSymKey(key);
|
||||
|
||||
if (ecx)
|
||||
{
|
||||
SECStatus srv;
|
||||
|
||||
PromiseFlatCString fClearText(aClearText);
|
||||
srv = SEC_PKCS7EncoderUpdate(ecx, fClearText.get(), fClearText.Length());
|
||||
|
||||
SEC_PKCS7EncoderFinish(ecx, nsnull, nsnull);
|
||||
|
||||
if (SECSuccess == srv)
|
||||
rv = EncodeBase64(result, aCipherText);
|
||||
}
|
||||
else
|
||||
{
|
||||
NS_WARNING("Could not create PKCS#7 encoder context");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SEC_PKCS7DestroyContentInfo(cinfo);
|
||||
return rv;
|
||||
*aBits = mKeypairBits;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WeaveCrypto::Decrypt(const nsACString& aPass,
|
||||
const nsACString& aCipherText,
|
||||
WeaveCrypto::SetKeypairBits(PRUint32 aBits)
|
||||
{
|
||||
mKeypairBits = aBits;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Encrypt
|
||||
*/
|
||||
NS_IMETHODIMP
|
||||
WeaveCrypto::Encrypt(const nsACString& aClearText,
|
||||
const nsACString& aSymmetricKey,
|
||||
const nsACString& aIV,
|
||||
nsACString& aCipherText)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
// When using CBC padding, the output is 1 block larger than the input.
|
||||
CK_MECHANISM_TYPE mech = PK11_AlgtagToMechanism(mAlgorithm);
|
||||
PRUint32 blockSize = PK11_GetBlockSize(mech, nsnull);
|
||||
|
||||
char outputBuffer[aClearText.Length() + blockSize];
|
||||
PRUint32 outputBufferSize = sizeof(outputBuffer);
|
||||
PromiseFlatCString input(aClearText);
|
||||
|
||||
rv = CommonCrypt(input.get(), input.Length(),
|
||||
outputBuffer, &outputBufferSize,
|
||||
aSymmetricKey, aIV, CKA_ENCRYPT);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
EncodeBase64(outputBuffer, outputBufferSize, aCipherText);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Decrypt
|
||||
*/
|
||||
NS_IMETHODIMP
|
||||
WeaveCrypto::Decrypt(const nsACString& aCipherText,
|
||||
const nsACString& aSymmetricKey,
|
||||
const nsACString& aIV,
|
||||
nsACString& aClearText)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsCString fCipherText;
|
||||
rv = DecodeBase64(aCipherText, fCipherText);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
char inputBuffer[aCipherText.Length()];
|
||||
PRUint32 inputBufferSize = sizeof(inputBuffer);
|
||||
char outputBuffer[aCipherText.Length()];
|
||||
PRUint32 outputBufferSize = sizeof(outputBuffer);
|
||||
|
||||
aClearText.Truncate();
|
||||
rv = DecodeBase64(aCipherText, inputBuffer, &inputBufferSize);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PromiseFlatCString fPass(aPass);
|
||||
SECItem pwitem = {siBuffer, (unsigned char*)fPass.get(), fPass.Length()};
|
||||
SEC_PKCS7DecoderContext *dcx = SEC_PKCS7DecoderStart(
|
||||
StoreToStringCallback, &aClearText, nsnull, nsnull,
|
||||
GetSymmetricKeyCallback, &pwitem,
|
||||
DecryptionAllowedCallback);
|
||||
NS_ENSURE_TRUE(dcx, NS_ERROR_FAILURE);
|
||||
rv = CommonCrypt(inputBuffer, inputBufferSize,
|
||||
outputBuffer, &outputBufferSize,
|
||||
aSymmetricKey, aIV, CKA_DECRYPT);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
SECStatus srv = SEC_PKCS7DecoderUpdate(dcx, fCipherText.get(), fCipherText.Length());
|
||||
rv = (SECSuccess == srv) ? NS_OK : NS_ERROR_FAILURE;
|
||||
aClearText.Assign(outputBuffer, outputBufferSize);
|
||||
|
||||
SEC_PKCS7ContentInfo *cinfo = SEC_PKCS7DecoderFinish(dcx);
|
||||
if (cinfo)
|
||||
SEC_PKCS7DestroyContentInfo(cinfo);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* CommonCrypt
|
||||
*/
|
||||
nsresult
|
||||
WeaveCrypto::CommonCrypt(const char *input, PRUint32 inputSize,
|
||||
char *output, PRUint32 *outputSize,
|
||||
const nsACString& aSymmetricKey,
|
||||
const nsACString& aIV,
|
||||
CK_ATTRIBUTE_TYPE aOperation)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
PK11SymKey *symKey = nsnull;
|
||||
PK11Context *ctx = nsnull;
|
||||
PK11SlotInfo *slot = nsnull;
|
||||
SECItem *ivParam = nsnull;
|
||||
|
||||
char keyData[aSymmetricKey.Length()];
|
||||
PRUint32 keyDataSize = sizeof(keyData);
|
||||
char ivData[aIV.Length()];
|
||||
PRUint32 ivDataSize = sizeof(ivData);
|
||||
|
||||
rv = DecodeBase64(aSymmetricKey, keyData, &keyDataSize);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = DecodeBase64(aIV, ivData, &ivDataSize);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
SECItem keyItem = {siBuffer, (unsigned char*)keyData, keyDataSize};
|
||||
SECItem ivItem = {siBuffer, (unsigned char*)ivData, ivDataSize};
|
||||
|
||||
|
||||
// AES_128_CBC --> CKM_AES_CBC --> CKM_AES_CBC_PAD
|
||||
CK_MECHANISM_TYPE mechanism = PK11_AlgtagToMechanism(mAlgorithm);
|
||||
mechanism = PK11_GetPadMechanism(mechanism);
|
||||
if (mechanism == CKM_INVALID_MECHANISM) {
|
||||
NS_WARNING("Unknown key mechanism");
|
||||
rv = NS_ERROR_FAILURE;
|
||||
goto crypt_done;
|
||||
}
|
||||
|
||||
ivParam = PK11_ParamFromIV(mechanism, &ivItem);
|
||||
if (!ivParam) {
|
||||
NS_WARNING("Couldn't create IV param");
|
||||
rv = NS_ERROR_FAILURE;
|
||||
goto crypt_done;
|
||||
}
|
||||
|
||||
slot = PK11_GetInternalKeySlot();
|
||||
if (!slot) {
|
||||
NS_WARNING("PK11_GetInternalKeySlot failed");
|
||||
rv = NS_ERROR_FAILURE;
|
||||
goto crypt_done;
|
||||
}
|
||||
|
||||
symKey = PK11_ImportSymKey(slot, mechanism, PK11_OriginUnwrap, aOperation, &keyItem, NULL);
|
||||
if (!symKey) {
|
||||
NS_WARNING("PK11_ImportSymKey failed");
|
||||
rv = NS_ERROR_FAILURE;
|
||||
goto crypt_done;
|
||||
}
|
||||
|
||||
ctx = PK11_CreateContextBySymKey(mechanism, aOperation, symKey, ivParam);
|
||||
if (!ctx) {
|
||||
NS_WARNING("PK11_CreateContextBySymKey failed");
|
||||
rv = NS_ERROR_FAILURE;
|
||||
goto crypt_done;
|
||||
}
|
||||
|
||||
int tmpOutSize; // XXX retarded. why is an signed int suddenly needed?
|
||||
PRUint32 maxOutputSize = *outputSize;
|
||||
rv = PK11_CipherOp(ctx,
|
||||
(unsigned char *)output, &tmpOutSize, maxOutputSize,
|
||||
(unsigned char *)input, inputSize);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("PK11_CipherOp failed");
|
||||
rv = NS_ERROR_FAILURE;
|
||||
goto crypt_done;
|
||||
}
|
||||
|
||||
*outputSize = tmpOutSize;
|
||||
output += tmpOutSize;
|
||||
maxOutputSize -= tmpOutSize;
|
||||
|
||||
// PK11_DigestFinal sure sounds like the last step for *hashing*, but it
|
||||
// just seems to be an odd name -- NSS uses this to finish the current
|
||||
// cipher operation. You'd think it would be called PK11_CipherOpFinal...
|
||||
PRUint32 tmpOutSize2; // XXX WTF? Now unsigned again? What kind of crazy API is this?
|
||||
rv = PK11_DigestFinal(ctx, (unsigned char *)output, &tmpOutSize2, maxOutputSize);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("PK11_DigestFinal failed");
|
||||
rv = NS_ERROR_FAILURE;
|
||||
goto crypt_done;
|
||||
}
|
||||
|
||||
*outputSize += tmpOutSize2;
|
||||
|
||||
crypt_done:
|
||||
if (ctx)
|
||||
PK11_DestroyContext(ctx, PR_TRUE);
|
||||
if (symKey)
|
||||
PK11_FreeSymKey(symKey);
|
||||
if (slot)
|
||||
PK11_FreeSlot(slot);
|
||||
if (ivParam)
|
||||
SECITEM_FreeItem(ivParam, PR_TRUE);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* GenerateKeypair
|
||||
*
|
||||
* Generates an RSA key pair, encrypts (wraps) the private key with the
|
||||
* supplied passphrase, and returns both to the caller.
|
||||
*
|
||||
* Based on code from:
|
||||
* http://www.mozilla.org/projects/security/pki/nss/sample-code/sample1.html
|
||||
*/
|
||||
NS_IMETHODIMP
|
||||
WeaveCrypto::GenerateKeypair(const nsACString& aPassphrase,
|
||||
const nsACString& aSalt,
|
||||
const nsACString& aIV,
|
||||
nsACString& aEncodedPublicKey,
|
||||
nsACString& aWrappedPrivateKey)
|
||||
{
|
||||
nsresult rv;
|
||||
SECStatus s;
|
||||
SECKEYPrivateKey *privKey = nsnull;
|
||||
SECKEYPublicKey *pubKey = nsnull;
|
||||
PK11SlotInfo *slot = nsnull;
|
||||
PK11RSAGenParams rsaParams;
|
||||
|
||||
|
||||
rsaParams.keySizeInBits = mKeypairBits; // 1024, 2048, etc.
|
||||
rsaParams.pe = 65537; // public exponent.
|
||||
|
||||
slot = PK11_GetInternalKeySlot();
|
||||
if (!slot) {
|
||||
NS_WARNING("PK11_GetInternalKeySlot failed");
|
||||
rv = NS_ERROR_FAILURE;
|
||||
goto keygen_done;
|
||||
}
|
||||
|
||||
// Generate the keypair.
|
||||
// XXX isSensitive sets PK11_ATTR_SENSITIVE | PK11_ATTR_PRIVATE
|
||||
// Might want to use PK11_GenerateKeyPairWithFlags and not set
|
||||
// CKA_PRIVATE, since that may trigger a master password entry, which is
|
||||
// kind of pointless for session objects...
|
||||
privKey = PK11_GenerateKeyPair(slot,
|
||||
CKM_RSA_PKCS_KEY_PAIR_GEN,
|
||||
&rsaParams, &pubKey,
|
||||
PR_FALSE, // isPerm
|
||||
PR_TRUE, // isSensitive
|
||||
nsnull); // wincx
|
||||
|
||||
if (!privKey) {
|
||||
NS_WARNING("PK11_GenerateKeyPair failed");
|
||||
rv = NS_ERROR_FAILURE;
|
||||
goto keygen_done;
|
||||
}
|
||||
|
||||
s = PK11_SetPrivateKeyNickname(privKey, "Weave User PrivKey");
|
||||
if (s != SECSuccess) {
|
||||
NS_WARNING("PK11_SetPrivateKeyNickname failed");
|
||||
rv = NS_ERROR_FAILURE;
|
||||
goto keygen_done;
|
||||
}
|
||||
|
||||
|
||||
rv = WrapPrivateKey(privKey, aPassphrase, aSalt, aIV, aWrappedPrivateKey);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("WrapPrivateKey failed");
|
||||
rv = NS_ERROR_FAILURE;
|
||||
goto keygen_done;
|
||||
}
|
||||
|
||||
rv = EncodePublicKey(pubKey, aEncodedPublicKey);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("EncodePublicKey failed");
|
||||
rv = NS_ERROR_FAILURE;
|
||||
goto keygen_done;
|
||||
}
|
||||
|
||||
keygen_done:
|
||||
// Cleanup allocated resources.
|
||||
if (pubKey)
|
||||
SECKEY_DestroyPublicKey(pubKey);
|
||||
if (privKey)
|
||||
SECKEY_DestroyPrivateKey(privKey);
|
||||
if (slot)
|
||||
PK11_FreeSlot(slot);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* DeriveKeyFromPassphrase
|
||||
*
|
||||
* Creates a symmertic key from a passphrase, using PKCS#5.
|
||||
*/
|
||||
nsresult
|
||||
WeaveCrypto::DeriveKeyFromPassphrase(const nsACString& aPassphrase,
|
||||
const nsACString& aSalt,
|
||||
PK11SymKey **aSymKey)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
PromiseFlatCString fPass(aPassphrase);
|
||||
SECItem passphrase = {siBuffer, (unsigned char *)fPass.get(), fPass.Length()};
|
||||
|
||||
char saltBytes[aSalt.Length()];
|
||||
PRUint32 saltBytesLength = aSalt.Length();
|
||||
rv = DecodeBase64(aSalt, saltBytes, &saltBytesLength);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
SECItem salt = {siBuffer, (unsigned char*)saltBytes, saltBytesLength};
|
||||
|
||||
// http://mxr.mozilla.org/seamonkey/source/security/nss/lib/pk11wrap/pk11pbe.c#1261
|
||||
|
||||
// Bug 436577 prevents us from just using SEC_OID_PKCS5_PBKDF2 here
|
||||
SECOidTag pbeAlg = mAlgorithm;
|
||||
SECOidTag cipherAlg = mAlgorithm; // ignored by callee when pbeAlg != a pkcs5 mech.
|
||||
SECOidTag prfAlg = SEC_OID_HMAC_SHA1; // callee picks if SEC_OID_UNKNOWN, but only SHA1 is supported
|
||||
|
||||
PRInt32 keyLength = 0; // Callee will pick.
|
||||
PRInt32 iterations = 4096; // PKCS#5 recommends at least 1000.
|
||||
|
||||
SECAlgorithmID *algid = PK11_CreatePBEV2AlgorithmID(pbeAlg, cipherAlg, prfAlg,
|
||||
keyLength, iterations, &salt);
|
||||
if (!algid)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
PK11SlotInfo *slot = PK11_GetInternalSlot();
|
||||
if (!slot)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
*aSymKey = PK11_PBEKeyGen(slot, algid, &passphrase, PR_FALSE, nsnull);
|
||||
|
||||
SECOID_DestroyAlgorithmID(algid, PR_TRUE);
|
||||
PK11_FreeSlot(slot);
|
||||
|
||||
return (*aSymKey ? NS_OK : NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* WrapPrivateKey
|
||||
*
|
||||
* Extract the private key by wrapping it (ie, converting it to a binary
|
||||
* blob and encoding it with a symmetric key)
|
||||
*
|
||||
* See:
|
||||
* http://www.mozilla.org/projects/security/pki/nss/tech-notes/tn5.html,
|
||||
* "Symmetric Key Wrapping/Unwrapping of a Private Key"
|
||||
*
|
||||
* The NSS softtoken uses sftk_PackagePrivateKey() to prepare the private key
|
||||
* for wrapping. For RSA, that's an ASN1 encoded PKCS1 object.
|
||||
*/
|
||||
nsresult
|
||||
WeaveCrypto::WrapPrivateKey(SECKEYPrivateKey *aPrivateKey,
|
||||
const nsACString& aPassphrase,
|
||||
const nsACString& aSalt,
|
||||
const nsACString& aIV,
|
||||
nsACString& aWrappedPrivateKey)
|
||||
|
||||
{
|
||||
nsresult rv;
|
||||
SECStatus s;
|
||||
PK11SymKey *pbeKey = nsnull;
|
||||
|
||||
// Convert our passphrase to a symkey and get the IV in the form we want.
|
||||
rv = DeriveKeyFromPassphrase(aPassphrase, aSalt, &pbeKey);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
char ivData[aIV.Length()];
|
||||
PRUint32 ivDataSize = sizeof(ivData);
|
||||
rv = DecodeBase64(aIV, ivData, &ivDataSize);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
SECItem ivItem = {siBuffer, (unsigned char*)ivData, ivDataSize};
|
||||
|
||||
// AES_128_CBC --> CKM_AES_CBC --> CKM_AES_CBC_PAD
|
||||
CK_MECHANISM_TYPE wrapMech = PK11_AlgtagToMechanism(mAlgorithm);
|
||||
wrapMech = PK11_GetPadMechanism(wrapMech);
|
||||
if (wrapMech == CKM_INVALID_MECHANISM) {
|
||||
NS_WARNING("Unknown key mechanism");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
SECItem *ivParam = PK11_ParamFromIV(wrapMech, &ivItem);
|
||||
if (!ivParam) {
|
||||
NS_WARNING("Couldn't create IV param");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
// Use a stack buffer to hold the wrapped key. NSS says about 1200 bytes for
|
||||
// a 2048-bit RSA key, so our 4096 byte buffer should be plenty.
|
||||
unsigned char stackBuffer[4096];
|
||||
SECItem wrappedKey = {siBuffer, stackBuffer, sizeof(stackBuffer)};
|
||||
|
||||
s = PK11_WrapPrivKey(aPrivateKey->pkcs11Slot,
|
||||
pbeKey, aPrivateKey,
|
||||
wrapMech, ivParam,
|
||||
&wrappedKey, nsnull);
|
||||
|
||||
SECITEM_FreeItem(ivParam, PR_TRUE);
|
||||
PK11_FreeSymKey(pbeKey);
|
||||
|
||||
if (s != SECSuccess) {
|
||||
NS_WARNING("PK11_WrapPrivKey failed");
|
||||
return(NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
EncodeBase64((char *)wrappedKey.data, wrappedKey.len, aWrappedPrivateKey);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* EncodePublicKey
|
||||
*
|
||||
* http://svn.mozilla.org/projects/mccoy/trunk/components/src/KeyPair.cpp
|
||||
*/
|
||||
nsresult
|
||||
WeaveCrypto::EncodePublicKey(SECKEYPublicKey *aPublicKey,
|
||||
nsACString& aEncodedPublicKey)
|
||||
{
|
||||
//SECItem *derKey = PK11_DEREncodePublicKey(aPublicKey);
|
||||
SECItem *derKey = SECKEY_EncodeDERSubjectPublicKeyInfo(aPublicKey);
|
||||
if (!derKey)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
EncodeBase64((char *)derKey->data, derKey->len, aEncodedPublicKey);
|
||||
|
||||
// XXX destroy derKey?
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* GenerateRandomBytes
|
||||
*/
|
||||
NS_IMETHODIMP
|
||||
WeaveCrypto::GenerateRandomBytes(PRUint32 aByteCount,
|
||||
nsACString& aEncodedBytes)
|
||||
{
|
||||
nsresult rv;
|
||||
char random[aByteCount];
|
||||
|
||||
rv = PK11_GenerateRandom((unsigned char *)random, aByteCount);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
EncodeBase64(random, aByteCount, aEncodedBytes);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* GenerateRandomIV
|
||||
*/
|
||||
NS_IMETHODIMP
|
||||
WeaveCrypto::GenerateRandomIV(nsACString& aEncodedBytes)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
CK_MECHANISM_TYPE mech = PK11_AlgtagToMechanism(mAlgorithm);
|
||||
PRUint32 size = PK11_GetIVLength(mech);
|
||||
|
||||
char random[size];
|
||||
|
||||
rv = PK11_GenerateRandom((unsigned char *)random, size);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
EncodeBase64(random, size, aEncodedBytes);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* GenerateRandomKey
|
||||
*
|
||||
* Generates a random symmetric key, and returns it in base64 encoded form.
|
||||
*
|
||||
* Note that we could just use GenerateRandomBytes to do all this (at least
|
||||
* for AES), but ideally we'd be able to switch to passing around key handles
|
||||
* insead of the actual key bits, which would require generating an actual
|
||||
* key.
|
||||
*/
|
||||
NS_IMETHODIMP
|
||||
WeaveCrypto::GenerateRandomKey(nsACString& aEncodedKey)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
PRUint32 keySize;
|
||||
CK_MECHANISM_TYPE keygenMech;
|
||||
|
||||
// XXX doesn't NSS have a lookup function to do this?
|
||||
switch (mAlgorithm) {
|
||||
case AES_128_CBC:
|
||||
keygenMech = CKM_AES_KEY_GEN;
|
||||
keySize = 16;
|
||||
break;
|
||||
|
||||
case AES_192_CBC:
|
||||
keygenMech = CKM_AES_KEY_GEN;
|
||||
keySize = 24;
|
||||
break;
|
||||
|
||||
case AES_256_CBC:
|
||||
keygenMech = CKM_AES_KEY_GEN;
|
||||
keySize = 32;
|
||||
break;
|
||||
|
||||
default:
|
||||
NS_WARNING("Unknown random keygen algorithm");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
PK11SlotInfo *slot = PK11_GetInternalSlot();
|
||||
if (!slot)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
PK11SymKey* randKey = PK11_KeyGen(slot, keygenMech, NULL, keySize, NULL);
|
||||
if (!randKey) {
|
||||
NS_WARNING("PK11_KeyGen failed");
|
||||
rv = NS_ERROR_FAILURE;
|
||||
goto keygen_done;
|
||||
}
|
||||
|
||||
if (PK11_ExtractKeyValue(randKey)) {
|
||||
NS_WARNING("PK11_ExtractKeyValue failed");
|
||||
rv = NS_ERROR_FAILURE;
|
||||
goto keygen_done;
|
||||
}
|
||||
|
||||
SECItem *keydata = PK11_GetKeyData(randKey);
|
||||
if (!keydata) {
|
||||
NS_WARNING("PK11_GetKeyData failed");
|
||||
rv = NS_ERROR_FAILURE;
|
||||
goto keygen_done;
|
||||
}
|
||||
|
||||
EncodeBase64((char *)keydata->data, keydata->len, aEncodedKey);
|
||||
|
||||
keygen_done:
|
||||
// XXX does keydata need freed?
|
||||
if (randKey)
|
||||
PK11_FreeSymKey(randKey);
|
||||
if (slot)
|
||||
PK11_FreeSlot(slot);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* WrapSymmetricKey
|
||||
*/
|
||||
NS_IMETHODIMP
|
||||
WeaveCrypto::WrapSymmetricKey(const nsACString& aSymmetricKey,
|
||||
const nsACString& aPublicKey,
|
||||
nsACString& aWrappedKey)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
SECStatus s;
|
||||
PK11SlotInfo *slot = nsnull;
|
||||
PK11SymKey *symKey = nsnull;
|
||||
SECKEYPublicKey *pubKey = nsnull;
|
||||
CERTSubjectPublicKeyInfo *pubKeyInfo = nsnull;
|
||||
|
||||
// Step 1. Get rid of the base64 encoding on the inputs.
|
||||
|
||||
char publicKeyBuffer[aPublicKey.Length()];
|
||||
PRUint32 publicKeyBufferSize = aPublicKey.Length();
|
||||
rv = DecodeBase64(aPublicKey, publicKeyBuffer, &publicKeyBufferSize);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
SECItem pubKeyData = {siBuffer, (unsigned char *)publicKeyBuffer, publicKeyBufferSize};
|
||||
|
||||
char symKeyBuffer[aSymmetricKey.Length()];
|
||||
PRUint32 symKeyBufferSize = aSymmetricKey.Length();
|
||||
rv = DecodeBase64(aSymmetricKey, symKeyBuffer, &symKeyBufferSize);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
SECItem symKeyData = {siBuffer, (unsigned char *)symKeyBuffer, symKeyBufferSize};
|
||||
|
||||
char wrappedBuffer[4096];
|
||||
SECItem wrappedKey = {siBuffer, (unsigned char *)wrappedBuffer, sizeof(wrappedBuffer)};
|
||||
|
||||
|
||||
// Step 2. Put the symmetric key bits into a P11 key object.
|
||||
|
||||
slot = PK11_GetInternalSlot();
|
||||
if (!slot) {
|
||||
NS_WARNING("Can't get internal PK11 slot");
|
||||
rv = NS_ERROR_FAILURE;
|
||||
goto wrap_done;
|
||||
}
|
||||
|
||||
// ImportSymKey wants a mechanism, from which it derives the key type.
|
||||
CK_MECHANISM_TYPE keyMech = PK11_AlgtagToMechanism(mAlgorithm);
|
||||
if (keyMech == CKM_INVALID_MECHANISM) {
|
||||
NS_WARNING("Unknown key mechanism");
|
||||
rv = NS_ERROR_FAILURE;
|
||||
goto wrap_done;
|
||||
}
|
||||
|
||||
// This imports a key with the usage set for encryption, but that doesn't
|
||||
// really matter because we're just going to wrap it up and not use it.
|
||||
symKey = PK11_ImportSymKey(slot,
|
||||
keyMech,
|
||||
PK11_OriginUnwrap,
|
||||
CKA_ENCRYPT,
|
||||
&symKeyData,
|
||||
NULL);
|
||||
if (!symKey) {
|
||||
NS_WARNING("PK11_ImportSymKey failed");
|
||||
rv = NS_ERROR_FAILURE;
|
||||
goto wrap_done;
|
||||
}
|
||||
|
||||
|
||||
// Step 3. Put the public key bits into a P11 key object.
|
||||
|
||||
// Can't just do this directly, it's expecting a minimal ASN1 blob
|
||||
// pubKey = SECKEY_ImportDERPublicKey(&pubKeyData, CKK_RSA);
|
||||
pubKeyInfo = SECKEY_DecodeDERSubjectPublicKeyInfo(&pubKeyData);
|
||||
if (!pubKeyInfo) {
|
||||
NS_WARNING("SECKEY_DecodeDERSubjectPublicKeyInfo failed");
|
||||
rv = NS_ERROR_FAILURE;
|
||||
goto wrap_done;
|
||||
}
|
||||
|
||||
pubKey = SECKEY_ExtractPublicKey(pubKeyInfo);
|
||||
if (!pubKey) {
|
||||
NS_WARNING("SECKEY_ExtractPublicKey failed");
|
||||
rv = NS_ERROR_FAILURE;
|
||||
goto wrap_done;
|
||||
}
|
||||
|
||||
|
||||
// Step 4. Wrap the symmetric key with the public key.
|
||||
|
||||
CK_MECHANISM_TYPE wrapMech = PK11_AlgtagToMechanism(SEC_OID_PKCS1_RSA_ENCRYPTION);
|
||||
|
||||
s = PK11_PubWrapSymKey(wrapMech, pubKey, symKey, &wrappedKey);
|
||||
if (s != SECSuccess) {
|
||||
NS_WARNING("PK11_PubWrapSymKey failed");
|
||||
rv = NS_ERROR_FAILURE;
|
||||
goto wrap_done;
|
||||
}
|
||||
|
||||
|
||||
// Step 5. Base64 encode the wrapped key, cleanup, and return to caller.
|
||||
|
||||
EncodeBase64((char *)wrappedKey.data, wrappedKey.len, aWrappedKey);
|
||||
|
||||
wrap_done:
|
||||
if (pubKey)
|
||||
SECKEY_DestroyPublicKey(pubKey);
|
||||
if (pubKeyInfo)
|
||||
SECKEY_DestroySubjectPublicKeyInfo(pubKeyInfo);
|
||||
if (symKey)
|
||||
PK11_FreeSymKey(symKey);
|
||||
if (slot)
|
||||
PK11_FreeSlot(slot);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* UnwrapSymmetricKey
|
||||
*/
|
||||
NS_IMETHODIMP
|
||||
WeaveCrypto::UnwrapSymmetricKey(const nsACString& aWrappedSymmetricKey,
|
||||
const nsACString& aWrappedPrivateKey,
|
||||
const nsACString& aPassphrase,
|
||||
const nsACString& aSalt,
|
||||
const nsACString& aIV,
|
||||
nsACString& aSymmetricKey)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
PK11SlotInfo *slot = nsnull;
|
||||
PK11SymKey *pbeKey = nsnull;
|
||||
PK11SymKey *symKey = nsnull;
|
||||
SECKEYPrivateKey *privKey = nsnull;
|
||||
SECItem *ivParam = nsnull;
|
||||
|
||||
CK_ATTRIBUTE_TYPE privKeyUsage[] = { CKA_UNWRAP };
|
||||
PRUint32 privKeyUsageLength = sizeof(privKeyUsage) / sizeof(CK_ATTRIBUTE_TYPE);
|
||||
|
||||
|
||||
// Step 1. Get rid of the base64 encoding on the inputs.
|
||||
|
||||
char privateKeyBuffer[aWrappedPrivateKey.Length()];
|
||||
PRUint32 privateKeyBufferSize = aWrappedPrivateKey.Length();
|
||||
rv = DecodeBase64(aWrappedPrivateKey, privateKeyBuffer, &privateKeyBufferSize);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
SECItem wrappedPrivKey = {siBuffer, (unsigned char *)privateKeyBuffer, privateKeyBufferSize};
|
||||
|
||||
char wrappedKeyBuffer[aWrappedSymmetricKey.Length()];
|
||||
PRUint32 wrappedKeyBufferSize = aWrappedSymmetricKey.Length();
|
||||
rv = DecodeBase64(aWrappedSymmetricKey, wrappedKeyBuffer, &wrappedKeyBufferSize);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
SECItem wrappedSymKey = {siBuffer, (unsigned char *)wrappedKeyBuffer, wrappedKeyBufferSize};
|
||||
|
||||
|
||||
// Step 2. Convert the passphrase to a symmetric key and get the IV in the proper form.
|
||||
rv = DeriveKeyFromPassphrase(aPassphrase, aSalt, &pbeKey);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
char ivData[aIV.Length()];
|
||||
PRUint32 ivDataSize = sizeof(ivData);
|
||||
rv = DecodeBase64(aIV, ivData, &ivDataSize);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
SECItem ivItem = {siBuffer, (unsigned char*)ivData, ivDataSize};
|
||||
|
||||
// AES_128_CBC --> CKM_AES_CBC --> CKM_AES_CBC_PAD
|
||||
CK_MECHANISM_TYPE wrapMech = PK11_AlgtagToMechanism(mAlgorithm);
|
||||
wrapMech = PK11_GetPadMechanism(wrapMech);
|
||||
if (wrapMech == CKM_INVALID_MECHANISM) {
|
||||
NS_WARNING("Unknown key mechanism");
|
||||
rv = NS_ERROR_FAILURE;
|
||||
goto unwrap_done;
|
||||
}
|
||||
|
||||
ivParam = PK11_ParamFromIV(wrapMech, &ivItem);
|
||||
if (!ivParam) {
|
||||
NS_WARNING("Couldn't create IV param");
|
||||
rv = NS_ERROR_FAILURE;
|
||||
goto unwrap_done;
|
||||
}
|
||||
|
||||
|
||||
// Step 3. Unwrap the private key with the key from the passphrase.
|
||||
|
||||
slot = PK11_GetInternalSlot();
|
||||
if (!slot) {
|
||||
NS_WARNING("Can't get internal PK11 slot");
|
||||
rv = NS_ERROR_FAILURE;
|
||||
goto unwrap_done;
|
||||
}
|
||||
|
||||
|
||||
// Normally, one wants to associate a private key with a public key.
|
||||
// P11_UnwrapPrivKey() passes its keyID arg to PK11_MakeIDFromPubKey(),
|
||||
// which hashes the public key to create an ID (or, for small inputs,
|
||||
// assumes it's already hashed and does nothing).
|
||||
// We don't really care about this, because our unwrapped private key will
|
||||
// just live long enough to unwrap the bulk data key. So, we'll just jam in
|
||||
// a random value... We have an IV handy, so that will suffice.
|
||||
SECItem *keyID = &ivItem;
|
||||
|
||||
privKey = PK11_UnwrapPrivKey(slot,
|
||||
pbeKey, wrapMech, ivParam, &wrappedPrivKey,
|
||||
nsnull, // label (SECItem)
|
||||
keyID,
|
||||
PR_FALSE, // isPerm (token object)
|
||||
PR_TRUE, // isSensitive
|
||||
CKK_RSA,
|
||||
privKeyUsage, privKeyUsageLength,
|
||||
nsnull); // wincx
|
||||
if (!privKey) {
|
||||
NS_WARNING("PK11_UnwrapPrivKey failed");
|
||||
rv = NS_ERROR_FAILURE;
|
||||
goto unwrap_done;
|
||||
}
|
||||
|
||||
// Step 4. Unwrap the symmetric key with the user's private key.
|
||||
|
||||
// XXX also have PK11_PubUnwrapSymKeyWithFlags() if more control is needed.
|
||||
// (last arg is keySize, 0 seems to work)
|
||||
symKey = PK11_PubUnwrapSymKey(privKey, &wrappedSymKey, wrapMech,
|
||||
CKA_DECRYPT, 0);
|
||||
if (!symKey) {
|
||||
NS_WARNING("PK11_PubUnwrapSymKey failed");
|
||||
rv = NS_ERROR_FAILURE;
|
||||
goto unwrap_done;
|
||||
}
|
||||
|
||||
// Step 5. Base64 encode the unwrapped key, cleanup, and return to caller.
|
||||
|
||||
if (PK11_ExtractKeyValue(symKey)) {
|
||||
NS_WARNING("PK11_ExtractKeyValue failed");
|
||||
rv = NS_ERROR_FAILURE;
|
||||
goto unwrap_done;
|
||||
}
|
||||
|
||||
// XXX need to free this?
|
||||
SECItem *symKeyData = PK11_GetKeyData(symKey);
|
||||
if (!symKeyData) {
|
||||
NS_WARNING("PK11_GetKeyData failed");
|
||||
rv = NS_ERROR_FAILURE;
|
||||
goto unwrap_done;
|
||||
}
|
||||
|
||||
EncodeBase64((char *)symKeyData->data, symKeyData->len, aSymmetricKey);
|
||||
|
||||
unwrap_done:
|
||||
if (privKey)
|
||||
SECKEY_DestroyPrivateKey(privKey);
|
||||
if (symKey)
|
||||
PK11_FreeSymKey(symKey);
|
||||
if (pbeKey)
|
||||
PK11_FreeSymKey(pbeKey);
|
||||
if (slot)
|
||||
PK11_FreeSlot(slot);
|
||||
if (ivParam)
|
||||
SECITEM_FreeItem(ivParam, PR_TRUE);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
@ -44,7 +44,7 @@
|
||||
#include "pk11pub.h"
|
||||
|
||||
#define WEAVE_CRYPTO_CONTRACTID "@labs.mozilla.com/Weave/Crypto;1"
|
||||
#define WEAVE_CRYPTO_CLASSNAME "A Simple XPCOM Sample"
|
||||
#define WEAVE_CRYPTO_CLASSNAME "Weave crypto module"
|
||||
#define WEAVE_CRYPTO_CID { 0xd3b0f750, 0xc976, 0x46d0, \
|
||||
{ 0xbe, 0x20, 0x96, 0xb2, 0x4f, 0x46, 0x84, 0xbc } }
|
||||
|
||||
@ -60,13 +60,33 @@ private:
|
||||
~WeaveCrypto();
|
||||
|
||||
SECOidTag mAlgorithm;
|
||||
PRUint32 mKeypairBits;
|
||||
|
||||
nsresult DecodeBase64(const nsACString& base64, nsACString& retval);
|
||||
nsresult EncodeBase64(const nsACString& binary, nsACString& retval);
|
||||
nsresult DecodeBase64(const nsACString& base64, char *aData, PRUint32 *aLength);
|
||||
void EncodeBase64(const nsACString& binary, nsACString& retval);
|
||||
void EncodeBase64(const char *aData, PRUint32 aLength, nsACString& retval);
|
||||
|
||||
nsresult CommonCrypt(const char *input, PRUint32 inputSize,
|
||||
char *output, PRUint32 *outputSize,
|
||||
const nsACString& aSymmetricKey,
|
||||
const nsACString& aIV,
|
||||
CK_ATTRIBUTE_TYPE aOperation);
|
||||
|
||||
|
||||
nsresult DeriveKeyFromPassphrase(const nsACString& aPassphrase,
|
||||
const nsACString& aSalt,
|
||||
PK11SymKey **aSymKey);
|
||||
|
||||
nsresult WrapPrivateKey(SECKEYPrivateKey *aPrivateKey,
|
||||
const nsACString& aPassphrase,
|
||||
const nsACString& aSalt,
|
||||
const nsACString& aIV,
|
||||
nsACString& aEncodedPublicKey);
|
||||
nsresult EncodePublicKey(SECKEYPublicKey *aPublicKey,
|
||||
nsACString& aEncodedPublicKey);
|
||||
|
||||
|
||||
static void StoreToStringCallback(void *arg, const char *buf, unsigned long len);
|
||||
static PK11SymKey *GetSymmetricKeyCallback(void *arg, SECAlgorithmID *algid);
|
||||
static PRBool DecryptionAllowedCallback(SECAlgorithmID *algid, PK11SymKey *key);
|
||||
};
|
||||
|
||||
#endif // WeaveCrypto_h_
|
||||
|
@ -14,6 +14,13 @@
|
||||
<!ENTITY signUpForAccount.description "To use Weave you must first create an account.">
|
||||
<!ENTITY createAccountButton.label "Create Account...">
|
||||
<!ENTITY accountSettings.label "Settings">
|
||||
<!ENTITY changePassword.caption "Change Password">
|
||||
<!ENTITY changePassword.description "Change the password you use to sign into Weave.">
|
||||
<!ENTITY oldPassword.label "Current Password:">
|
||||
<!ENTITY newPassword.label "New Password:">
|
||||
<!ENTITY newPasswordAgain.label "Re-enter:">
|
||||
<!ENTITY showPasswords.label "Show Passwords">
|
||||
<!ENTITY changePasswordButton.label "Change">
|
||||
|
||||
<!ENTITY backUpCheckbox.label "Back up and synchronize my data to a Weave server">
|
||||
<!ENTITY encryptOnServerCheckbox.label "Encrypt my data on the server">
|
||||
|
@ -7,4 +7,12 @@ reset.client.warning = This will permanently delete all of your bookmarks and br
|
||||
reset.login.warning.title = Reset Login
|
||||
reset.login.warning = This will permanently remove your username, password and passphrase from this instance of Firefox.\n\nAre you sure you want to do this?
|
||||
reset.lock.warning.title = Reset Server Lock
|
||||
reset.lock.warning = This will reset the write lock on the Weave server for your account. To avoid corruption, you should only do this if a lock has become stale.\n\nAre you sure you want to do this?
|
||||
reset.lock.warning = This will reset the write lock on the Weave server for your account. To avoid corruption, you should only do this if a lock has become stale.\n\nAre you sure you want to do this?
|
||||
|
||||
change.password.status.active = Changing your password...
|
||||
change.password.status.success = Your password has been changed.
|
||||
change.password.status.error = There was an error changing your password.
|
||||
change.password.status.passwordsDoNotMatch = The passwords you entered do not match.
|
||||
change.password.status.noOldPassword = You didn't enter your current password.
|
||||
change.password.status.noNewPassword = You didn't enter a new password.
|
||||
change.password.status.badOldPassword = Your current password is incorrect.
|
||||
|
@ -48,7 +48,8 @@ Cu.import("resource://weave/util.js");
|
||||
* Asynchronous generator helpers
|
||||
*/
|
||||
|
||||
let currentId = 0;
|
||||
let gCurrentId = 0;
|
||||
let gOutstandingGenerators = 0;
|
||||
|
||||
function AsyncException(initFrame, message) {
|
||||
this.message = message;
|
||||
@ -71,13 +72,14 @@ AsyncException.prototype = {
|
||||
};
|
||||
|
||||
function Generator(thisArg, method, onComplete, args) {
|
||||
gOutstandingGenerators++;
|
||||
this._outstandingCbs = 0;
|
||||
this._log = Log4Moz.Service.getLogger("Async.Generator");
|
||||
this._log.level =
|
||||
Log4Moz.Level[Utils.prefs.getCharPref("log.logger.async")];
|
||||
this._thisArg = thisArg;
|
||||
this._method = method;
|
||||
this._id = currentId++;
|
||||
this._id = gCurrentId++;
|
||||
this.onComplete = onComplete;
|
||||
this._args = args;
|
||||
this._initFrame = Components.stack.caller;
|
||||
@ -251,6 +253,7 @@ Generator.prototype = {
|
||||
this._log.trace("Initial stack trace:\n" + this.trace);
|
||||
}
|
||||
}
|
||||
gOutstandingGenerators--;
|
||||
}
|
||||
};
|
||||
|
||||
@ -280,6 +283,7 @@ function trace(frame, str) {
|
||||
|
||||
|
||||
Async = {
|
||||
get outstandingGenerators() { return gOutstandingGenerators; },
|
||||
|
||||
// Use:
|
||||
// let gen = Async.run(this, this.fooGen, ...);
|
||||
|
@ -66,16 +66,11 @@ CryptoSvc.prototype = {
|
||||
},
|
||||
|
||||
get defaultAlgorithm() {
|
||||
let branch = Cc["@mozilla.org/preferences-service;1"]
|
||||
.getService(Ci.nsIPrefBranch);
|
||||
return branch.getCharPref("extensions.weave.encryption");
|
||||
return Utils.prefs.getCharPref("encryption");
|
||||
},
|
||||
set defaultAlgorithm(value) {
|
||||
let branch = Cc["@mozilla.org/preferences-service;1"]
|
||||
.getService(Ci.nsIPrefBranch);
|
||||
let cur = branch.getCharPref("extensions.weave.encryption");
|
||||
if (value != cur)
|
||||
branch.setCharPref("extensions.weave.encryption", value);
|
||||
if (value != Utils.prefs.getCharPref("encryption"))
|
||||
Utils.prefs.setCharPref("encryption", value);
|
||||
},
|
||||
|
||||
_init: function Crypto__init() {
|
||||
|
@ -114,3 +114,88 @@ function makeAsyncTestRunner(generator) {
|
||||
|
||||
return run_test;
|
||||
}
|
||||
|
||||
function FakePrefService(contents) {
|
||||
Cu.import("resource://weave/util.js");
|
||||
this.fakeContents = contents;
|
||||
Utils.__prefs = this;
|
||||
}
|
||||
|
||||
FakePrefService.prototype = {
|
||||
_getPref: function fake__getPref(pref) {
|
||||
Log4Moz.Service.rootLogger.trace("Getting pref: " + pref);
|
||||
return this.fakeContents[pref];
|
||||
},
|
||||
getCharPref: function fake_getCharPref(pref) {
|
||||
return this._getPref(pref);
|
||||
},
|
||||
getBoolPref: function fake_getBoolPref(pref) {
|
||||
return this._getPref(pref);
|
||||
},
|
||||
getIntPref: function fake_getIntPref(pref) {
|
||||
return this._getPref(pref);
|
||||
},
|
||||
addObserver: function fake_addObserver() {}
|
||||
};
|
||||
|
||||
function makeFakeAsyncFunc(retval) {
|
||||
Cu.import("resource://weave/async.js");
|
||||
Function.prototype.async = Async.sugar;
|
||||
|
||||
function fakeAsyncFunc() {
|
||||
let self = yield;
|
||||
|
||||
Utils.makeTimerForCall(self.cb);
|
||||
yield;
|
||||
|
||||
self.done(retval);
|
||||
}
|
||||
|
||||
return fakeAsyncFunc;
|
||||
}
|
||||
|
||||
function FakeDAVService(contents) {
|
||||
Cu.import("resource://weave/dav.js");
|
||||
|
||||
this.fakeContents = contents;
|
||||
DAV.__proto__ = this;
|
||||
this.checkLogin = makeFakeAsyncFunc(true);
|
||||
}
|
||||
|
||||
FakeDAVService.prototype = {
|
||||
PUT: function fake_PUT(path, data, onComplete) {
|
||||
Log4Moz.Service.rootLogger.info("Putting " + path);
|
||||
this.fakeContents[path] = data;
|
||||
makeFakeAsyncFunc({status: 200}).async(this, onComplete);
|
||||
},
|
||||
|
||||
GET: function fake_GET(path, onComplete) {
|
||||
Log4Moz.Service.rootLogger.info("Retrieving " + path);
|
||||
var result = {status: 404};
|
||||
if (path in this.fakeContents)
|
||||
result = {status: 200, responseText: this.fakeContents[path]};
|
||||
|
||||
return makeFakeAsyncFunc(result).async(this, onComplete);
|
||||
},
|
||||
|
||||
MKCOL: function fake_MKCOL(path, onComplete) {
|
||||
Log4Moz.Service.rootLogger.info("Creating dir " + path);
|
||||
makeFakeAsyncFunc(true).async(this, onComplete);
|
||||
}
|
||||
};
|
||||
|
||||
function FakePasswordService(contents) {
|
||||
Cu.import("resource://weave/util.js");
|
||||
|
||||
this.fakeContents = contents;
|
||||
let self = this;
|
||||
|
||||
Utils.findPassword = function fake_findPassword(realm, username) {
|
||||
Log4Moz.Service.rootLogger.trace("Password requested for " +
|
||||
realm + ":" + username);
|
||||
if (realm in self.fakeContents && username in self.fakeContents[realm])
|
||||
return self.fakeContents[realm][username];
|
||||
else
|
||||
return null;
|
||||
};
|
||||
};
|
||||
|
@ -40,7 +40,11 @@ function run_test() {
|
||||
|
||||
do_check_eq(timesYielded, 2);
|
||||
|
||||
do_check_eq(Async.outstandingGenerators, 1);
|
||||
|
||||
do_check_true(fts.processCallback());
|
||||
|
||||
do_check_false(fts.processCallback());
|
||||
|
||||
do_check_eq(Async.outstandingGenerators, 0);
|
||||
}
|
||||
|
@ -47,4 +47,5 @@ function run_test() {
|
||||
runTestGenerator.async({});
|
||||
for (var i = 0; fts.processCallback(); i++) {}
|
||||
do_check_eq(i, 4);
|
||||
do_check_eq(Async.outstandingGenerators, 0);
|
||||
}
|
||||
|
@ -1,6 +1,23 @@
|
||||
Cu.import("resource://weave/util.js");
|
||||
Cu.import("resource://weave/async.js");
|
||||
Cu.import("resource://weave/dav.js");
|
||||
Cu.import("resource://weave/identity.js");
|
||||
|
||||
let __fakePasswords = {
|
||||
'Mozilla Services Password': {foo: "bar"},
|
||||
'Mozilla Services Encryption Passphrase': {foo: "passphrase"}
|
||||
};
|
||||
|
||||
function run_test() {
|
||||
var fpasses = new FakePasswordService(__fakePasswords);
|
||||
var fprefs = new FakePrefService({"encryption" : "none"});
|
||||
var fds = new FakeDAVService({});
|
||||
var fts = new FakeTimerService();
|
||||
var logStats = initTestLogging();
|
||||
|
||||
ID.set('Engine:PBE:default',
|
||||
new Identity('Mozilla Services Encryption Passphrase', 'foo'));
|
||||
|
||||
// The JS module we're testing, with all members exposed.
|
||||
var passwords = loadInSandbox("resource://weave/engines/passwords.js");
|
||||
|
||||
@ -24,6 +41,14 @@ function run_test() {
|
||||
return {exists: function() {return false;}};
|
||||
};
|
||||
|
||||
Utils.open = function fake_open(file, mode) {
|
||||
let fakeStream = {
|
||||
writeString: function(data) {Log4Moz.Service.rootLogger.debug(data);},
|
||||
close: function() {}
|
||||
};
|
||||
return [fakeStream];
|
||||
};
|
||||
|
||||
// Ensure that _hashLoginInfo() works.
|
||||
var fakeUserHash = passwords._hashLoginInfo(fakeUser);
|
||||
do_check_eq(typeof fakeUserHash, 'string');
|
||||
@ -34,6 +59,11 @@ function run_test() {
|
||||
do_check_false(psc._itemExists("invalid guid"));
|
||||
do_check_true(psc._itemExists(fakeUserHash));
|
||||
|
||||
// Make sure the engine can sync.
|
||||
var engine = new passwords.PasswordEngine();
|
||||
engine.sync();
|
||||
|
||||
while (fts.processCallback()) {}
|
||||
do_check_eq(logStats.errorsLogged, 0);
|
||||
do_check_eq(Async.outstandingGenerators, 0);
|
||||
}
|
||||
|
@ -1,59 +1,24 @@
|
||||
Cu.import("resource://weave/log4moz.js");
|
||||
Cu.import("resource://weave/wrap.js");
|
||||
Cu.import("resource://weave/async.js");
|
||||
Cu.import("resource://weave/util.js");
|
||||
Cu.import("resource://weave/dav.js");
|
||||
Cu.import("resource://weave/crypto.js");
|
||||
Cu.import("resource://weave/identity.js");
|
||||
|
||||
Function.prototype.async = Async.sugar;
|
||||
|
||||
function makeFakeAsyncFunc(retval) {
|
||||
function fakeAsyncFunc() {
|
||||
let self = yield;
|
||||
|
||||
Utils.makeTimerForCall(self.cb);
|
||||
yield;
|
||||
|
||||
self.done(retval);
|
||||
}
|
||||
|
||||
return fakeAsyncFunc;
|
||||
}
|
||||
|
||||
function FakePrefs() {}
|
||||
|
||||
FakePrefs.prototype = {
|
||||
__contents: {"log.logger.async" : "Debug",
|
||||
"username" : "foo",
|
||||
"serverURL" : "https://example.com/",
|
||||
"encryption" : true,
|
||||
"enabled" : true,
|
||||
"schedule" : 0},
|
||||
_getPref: function fake__getPref(pref) {
|
||||
Log4Moz.Service.rootLogger.trace("Getting pref: " + pref);
|
||||
return this.__contents[pref];
|
||||
},
|
||||
getCharPref: function fake_getCharPref(pref) {
|
||||
return this._getPref(pref);
|
||||
},
|
||||
getBoolPref: function fake_getBoolPref(pref) {
|
||||
return this._getPref(pref);
|
||||
},
|
||||
getIntPref: function fake_getIntPref(pref) {
|
||||
return this._getPref(pref);
|
||||
},
|
||||
addObserver: function fake_addObserver() {}
|
||||
let __fakePrefs = {
|
||||
"log.logger.async" : "Debug",
|
||||
"username" : "foo",
|
||||
"serverURL" : "https://example.com/",
|
||||
"encryption" : "aes-256-cbc",
|
||||
"enabled" : true,
|
||||
"schedule" : 0
|
||||
};
|
||||
|
||||
Utils.__prefs = new FakePrefs();
|
||||
let __fakeDAVContents = {
|
||||
"meta/version" : "2",
|
||||
"private/privkey" : "fake private key"
|
||||
};
|
||||
|
||||
Utils.findPassword = function fake_findPassword(realm, username) {
|
||||
let contents = {
|
||||
'Mozilla Services Password': {foo: "bar"},
|
||||
'Mozilla Services Encryption Passphrase': {foo: "passphrase"}
|
||||
};
|
||||
return contents[realm][username];
|
||||
let __fakePasswords = {
|
||||
'Mozilla Services Password': {foo: "bar"},
|
||||
'Mozilla Services Encryption Passphrase': {foo: "passphrase"}
|
||||
};
|
||||
|
||||
Crypto.__proto__ = {
|
||||
@ -68,22 +33,6 @@ Crypto.__proto__ = {
|
||||
}
|
||||
};
|
||||
|
||||
DAV.__proto__ = {
|
||||
checkLogin: makeFakeAsyncFunc(true),
|
||||
|
||||
__contents: {"meta/version" : "2",
|
||||
"private/privkey" : "fake private key"},
|
||||
|
||||
GET: function fake_GET(path, onComplete) {
|
||||
Log4Moz.Service.rootLogger.info("Retrieving " + path);
|
||||
var result = {status: 404};
|
||||
if (path in this.__contents)
|
||||
result = {status: 200, responseText: this.__contents[path]};
|
||||
|
||||
return makeFakeAsyncFunc(result).async(this, onComplete);
|
||||
}
|
||||
};
|
||||
|
||||
let Service = loadInSandbox("resource://weave/service.js");
|
||||
|
||||
function TestService() {
|
||||
@ -99,6 +48,9 @@ TestService.prototype = {
|
||||
TestService.prototype.__proto__ = Service.WeaveSvc.prototype;
|
||||
|
||||
function test_login_works() {
|
||||
var fds = new FakeDAVService(__fakeDAVContents);
|
||||
var fprefs = new FakePrefService(__fakePrefs);
|
||||
var fpasses = new FakePasswordService(__fakePasswords);
|
||||
var fts = new FakeTimerService();
|
||||
var logStats = initTestLogging();
|
||||
var testService = new TestService();
|
||||
@ -116,4 +68,5 @@ function test_login_works() {
|
||||
do_check_true(finished);
|
||||
do_check_true(successful);
|
||||
do_check_eq(logStats.errorsLogged, 0);
|
||||
do_check_eq(Async.outstandingGenerators, 0);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user