This commit is contained in:
jonathandicarlo@jonathan-dicarlos-macbook-pro.local 2008-06-19 10:51:54 -07:00
commit 5f7b9953fd
12 changed files with 1132 additions and 225 deletions

View File

@ -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);
};

View File

@ -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;
}

View File

@ -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_

View File

@ -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">

View File

@ -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.

View File

@ -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, ...);

View File

@ -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() {

View File

@ -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;
};
};

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}