зеркало из https://github.com/mozilla/pjs.git
Bug 415799: Interface and implementation for HMAC support. p=honzab@allpeers.com (Jan Bambas), r=kaie, r=rrelyea, sr=dveditz, blocking1.9=sayrer
This commit is contained in:
Родитель
71bfd45913
Коммит
da8f86f15b
|
@ -71,6 +71,7 @@ XPIDLSRCS = \
|
|||
nsIBufferedStreams.idl \
|
||||
nsICancelable.idl \
|
||||
nsICryptoHash.idl \
|
||||
nsICryptoHMAC.idl \
|
||||
nsIDownloader.idl \
|
||||
nsIEncodedChannel.idl \
|
||||
nsIFileStreams.idl \
|
||||
|
|
|
@ -0,0 +1,139 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Doug Turner <dougt@meer.net>.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2005
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Doug Turner <dougt@meer.net>
|
||||
* Honza Bambas <honzab@firemni.cz>
|
||||
*
|
||||
* 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
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
interface nsIInputStream;
|
||||
|
||||
/**
|
||||
* nsICryptoHMAC
|
||||
* This interface provides HMAC signature algorithms.
|
||||
*/
|
||||
|
||||
[scriptable, uuid(B8E1AC84-E10D-47fe-9D03-F0AF2E943E71)]
|
||||
interface nsICryptoHMAC : nsISupports
|
||||
{
|
||||
/**
|
||||
* Hashing Algorithms. These values are to be used by the
|
||||
* |init| method to indicate which hashing function to
|
||||
* use. These values map onto the values defined in
|
||||
* mozilla/security/nss/lib/softoken/pkcs11t.h and are
|
||||
* switched to CKM_*_HMAC constant.
|
||||
*/
|
||||
const short MD2 = 1;
|
||||
const short MD5 = 2;
|
||||
const short SHA1 = 3;
|
||||
const short SHA256 = 4;
|
||||
const short SHA384 = 5;
|
||||
const short SHA512 = 6;
|
||||
|
||||
/**
|
||||
* Initialize the hashing object. This method may be
|
||||
* called multiple times with different algorithm types.
|
||||
*
|
||||
* @param aAlgorithm the algorithm type to be used.
|
||||
* This value must be one of the above valid
|
||||
* algorithm types.
|
||||
*
|
||||
* @param aKeyData the raw key data used for the HMAC
|
||||
* computation
|
||||
*
|
||||
* @param aKeyLength length of the key in bytes
|
||||
*
|
||||
* @throws NS_ERROR_INVALID_ARG if an unsupported algorithm
|
||||
* type is passed.
|
||||
*
|
||||
* NOTE: This method must be called before any other method
|
||||
* on this interface is called.
|
||||
*/
|
||||
void init(in unsigned long aAlgorithm, [const, array, size_is(aKeyLen)] in octet aKeyData,
|
||||
in unsigned long aKeyLen);
|
||||
|
||||
/**
|
||||
* @param aData a buffer to calculate the hash over
|
||||
*
|
||||
* @param aLen the length of the buffer |aData|
|
||||
*
|
||||
* @throws NS_ERROR_NOT_INITIALIZED if |init| has not been
|
||||
* called.
|
||||
*/
|
||||
void update([const, array, size_is(aLen)] in octet aData, in unsigned long aLen);
|
||||
|
||||
/**
|
||||
* Calculates and updates a new hash based on a given data stream.
|
||||
*
|
||||
* @param aStream an input stream to read from.
|
||||
*
|
||||
* @param aLen how much to read from the given |aStream|. Passing
|
||||
* PR_UINT32_MAX indicates that all data available will be used
|
||||
* to update the hash.
|
||||
*
|
||||
* @throws NS_ERROR_NOT_INITIALIZED if |init| has not been
|
||||
* called.
|
||||
*
|
||||
* @throws NS_ERROR_NOT_AVAILABLE if the requested amount of
|
||||
* data to be calculated into the hash is not available.
|
||||
*
|
||||
*/
|
||||
void updateFromStream(in nsIInputStream aStream, in unsigned long aLen);
|
||||
|
||||
/**
|
||||
* Completes this HMAC object and produces the actual HMAC diegest data.
|
||||
*
|
||||
* @param aASCII if true then the returned value is a base-64
|
||||
* encoded string. if false, then the returned value is
|
||||
* binary data.
|
||||
*
|
||||
* @return a hash of the data that was read by this object. This can
|
||||
* be either binary data or base 64 encoded.
|
||||
*
|
||||
* @throws NS_ERROR_NOT_INITIALIZED if |init| has not been
|
||||
* called.
|
||||
*
|
||||
* NOTE: This method may be called any time after |init|
|
||||
* is called. This call resets the object to its
|
||||
* pre-init state.
|
||||
*/
|
||||
ACString finish(in PRBool aASCII);
|
||||
|
||||
/**
|
||||
* Reinitialize HMAC context to be reused with the same
|
||||
* settings (the key and hash algorithm) but on different
|
||||
* set of data.
|
||||
*/
|
||||
void reset();
|
||||
};
|
|
@ -846,6 +846,11 @@
|
|||
*/
|
||||
#define NS_CRYPTO_HASH_CONTRACTID "@mozilla.org/security/hash;1"
|
||||
|
||||
/**
|
||||
* Must implement nsICryptoHMAC.
|
||||
*/
|
||||
#define NS_CRYPTO_HMAC_CONTRACTID "@mozilla.org/security/hmac;1"
|
||||
|
||||
/******************************************************************************
|
||||
* Categories
|
||||
*/
|
||||
|
|
|
@ -2492,13 +2492,13 @@ nsCryptoHash::UpdateFromStream(nsIInputStream *data, PRUint32 len)
|
|||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
char buffer[NS_CRYPTO_HASH_BUFFER_SIZE];
|
||||
PRUint32 read;
|
||||
PRUint32 read, readLimit;
|
||||
|
||||
while(NS_SUCCEEDED(rv) && len>0)
|
||||
{
|
||||
read = PR_MIN(NS_CRYPTO_HASH_BUFFER_SIZE, len);
|
||||
readLimit = PR_MIN(NS_CRYPTO_HASH_BUFFER_SIZE, len);
|
||||
|
||||
rv = data->Read(buffer, read, &read);
|
||||
rv = data->Read(buffer, readLimit, &read);
|
||||
|
||||
if (NS_SUCCEEDED(rv))
|
||||
rv = Update((const PRUint8*)buffer, read);
|
||||
|
@ -2527,6 +2527,8 @@ nsCryptoHash::Finish(PRBool ascii, nsACString & _retval)
|
|||
if (ascii)
|
||||
{
|
||||
char *asciiData = BTOA_DataToAscii(buffer, hashLen);
|
||||
NS_ENSURE_TRUE(asciiData, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
_retval.Assign(asciiData);
|
||||
PORT_Free(asciiData);
|
||||
}
|
||||
|
@ -2538,6 +2540,178 @@ nsCryptoHash::Finish(PRBool ascii, nsACString & _retval)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
//---------------------------------------------
|
||||
// Implementing nsICryptoHMAC
|
||||
//---------------------------------------------
|
||||
|
||||
NS_IMPL_ISUPPORTS1(nsCryptoHMAC, nsICryptoHMAC)
|
||||
|
||||
nsCryptoHMAC::nsCryptoHMAC()
|
||||
{
|
||||
mHMACContext = nsnull;
|
||||
}
|
||||
|
||||
nsCryptoHMAC::~nsCryptoHMAC()
|
||||
{
|
||||
if (mHMACContext)
|
||||
PK11_DestroyContext(mHMACContext, PR_TRUE);
|
||||
}
|
||||
|
||||
/* void init (in unsigned long aAlgorithm, in octet aKeyData, in long aKeyLength); */
|
||||
NS_IMETHODIMP nsCryptoHMAC::Init(PRUint32 aAlgorithm, const PRUint8 *aKeyData, PRUint32 aKeyLen)
|
||||
{
|
||||
if (mHMACContext)
|
||||
{
|
||||
PK11_DestroyContext(mHMACContext, PR_TRUE);
|
||||
mHMACContext = nsnull;
|
||||
}
|
||||
|
||||
CK_MECHANISM_TYPE HMACMechType;
|
||||
switch (aAlgorithm)
|
||||
{
|
||||
case nsCryptoHMAC::MD2:
|
||||
HMACMechType = CKM_MD2_HMAC; break;
|
||||
case nsCryptoHMAC::MD5:
|
||||
HMACMechType = CKM_MD5_HMAC; break;
|
||||
case nsCryptoHMAC::SHA1:
|
||||
HMACMechType = CKM_SHA_1_HMAC; break;
|
||||
case nsCryptoHMAC::SHA256:
|
||||
HMACMechType = CKM_SHA256_HMAC; break;
|
||||
case nsCryptoHMAC::SHA384:
|
||||
HMACMechType = CKM_SHA384_HMAC; break;
|
||||
case nsCryptoHMAC::SHA512:
|
||||
HMACMechType = CKM_SHA512_HMAC; break;
|
||||
default:
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
PK11SlotInfo *slot = PK11_GetBestSlot(HMACMechType, nsnull);
|
||||
NS_ENSURE_TRUE(slot, NS_ERROR_FAILURE);
|
||||
|
||||
SECItem rawData;
|
||||
rawData.data = const_cast<unsigned char*>(aKeyData);
|
||||
rawData.len = aKeyLen;
|
||||
|
||||
PK11SymKey* key = PK11_ImportSymKey(
|
||||
slot, HMACMechType, PK11_OriginUnwrap, CKA_SIGN, &rawData, nsnull);
|
||||
PK11_FreeSlot(slot);
|
||||
|
||||
NS_ENSURE_TRUE(key, NS_ERROR_FAILURE);
|
||||
|
||||
rawData.data = 0;
|
||||
rawData.len = 0;
|
||||
mHMACContext = PK11_CreateContextBySymKey(
|
||||
HMACMechType, CKA_SIGN, key, &rawData);
|
||||
PK11_FreeSymKey(key);
|
||||
|
||||
NS_ENSURE_TRUE(mHMACContext, NS_ERROR_FAILURE);
|
||||
|
||||
SECStatus ss = PK11_DigestBegin(mHMACContext);
|
||||
NS_ENSURE_TRUE(ss == SECSuccess, NS_ERROR_FAILURE);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* void update ([array, size_is (aLen), const] in octet aData, in unsigned long aLen); */
|
||||
NS_IMETHODIMP nsCryptoHMAC::Update(const PRUint8 *aData, PRUint32 aLen)
|
||||
{
|
||||
if (!mHMACContext)
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (!aData)
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
|
||||
SECStatus ss = PK11_DigestOp(mHMACContext, aData, aLen);
|
||||
NS_ENSURE_TRUE(ss == SECSuccess, NS_ERROR_FAILURE);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* void updateFromStream (in nsIInputStream aStream, in unsigned long aLen); */
|
||||
NS_IMETHODIMP nsCryptoHMAC::UpdateFromStream(nsIInputStream *aStream, PRUint32 aLen)
|
||||
{
|
||||
if (!mHMACContext)
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (!aStream)
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
|
||||
PRUint32 n;
|
||||
nsresult rv = aStream->Available(&n);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
// if the user has passed PR_UINT32_MAX, then read
|
||||
// everything in the stream
|
||||
|
||||
if (aLen == PR_UINT32_MAX)
|
||||
aLen = n;
|
||||
|
||||
// So, if the stream has NO data available for the hash,
|
||||
// or if the data available is less then what the caller
|
||||
// requested, we can not fulfill the HMAC update. In this
|
||||
// case, just return NS_ERROR_NOT_AVAILABLE indicating
|
||||
// that there is not enough data in the stream to satisify
|
||||
// the request.
|
||||
|
||||
if (n == 0 || n < aLen)
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
char buffer[NS_CRYPTO_HASH_BUFFER_SIZE];
|
||||
PRUint32 read, readLimit;
|
||||
|
||||
while(NS_SUCCEEDED(rv) && aLen > 0)
|
||||
{
|
||||
readLimit = PR_MIN(NS_CRYPTO_HASH_BUFFER_SIZE, aLen);
|
||||
|
||||
rv = aStream->Read(buffer, readLimit, &read);
|
||||
if (read == 0)
|
||||
return NS_BASE_STREAM_CLOSED;
|
||||
|
||||
if (NS_SUCCEEDED(rv))
|
||||
rv = Update((const PRUint8*)buffer, read);
|
||||
|
||||
aLen -= read;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* ACString finish (in PRBool aASCII); */
|
||||
NS_IMETHODIMP nsCryptoHMAC::Finish(PRBool aASCII, nsACString & _retval)
|
||||
{
|
||||
if (!mHMACContext)
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
PRUint32 hashLen = 0;
|
||||
unsigned char buffer[HASH_LENGTH_MAX];
|
||||
unsigned char* pbuffer = buffer;
|
||||
|
||||
PK11_DigestFinal(mHMACContext, pbuffer, &hashLen, HASH_LENGTH_MAX);
|
||||
if (aASCII)
|
||||
{
|
||||
char *asciiData = BTOA_DataToAscii(buffer, hashLen);
|
||||
NS_ENSURE_TRUE(asciiData, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
_retval.Assign(asciiData);
|
||||
PORT_Free(asciiData);
|
||||
}
|
||||
else
|
||||
{
|
||||
_retval.Assign((const char*)buffer, hashLen);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* void reset (); */
|
||||
NS_IMETHODIMP nsCryptoHMAC::Reset()
|
||||
{
|
||||
SECStatus ss = PK11_DigestBegin(mHMACContext);
|
||||
NS_ENSURE_TRUE(ss == SECSuccess, NS_ERROR_FAILURE);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS1(PipUIContext, nsIInterfaceRequestor)
|
||||
|
||||
|
|
|
@ -64,6 +64,7 @@
|
|||
#include "nsHashtable.h"
|
||||
#include "prlock.h"
|
||||
#include "nsICryptoHash.h"
|
||||
#include "nsICryptoHMAC.h"
|
||||
#include "hasht.h"
|
||||
#include "nsNSSCallbacks.h"
|
||||
|
||||
|
@ -86,9 +87,12 @@
|
|||
#define NS_PSMCONTENTLISTEN_CID {0xc94f4a30, 0x64d7, 0x11d4, {0x99, 0x60, 0x00, 0xb0, 0xd0, 0x23, 0x54, 0xa0}}
|
||||
#define NS_PSMCONTENTLISTEN_CONTRACTID "@mozilla.org/security/psmdownload;1"
|
||||
|
||||
#define NS_CRYPTO_HASH_CLASSNAME "Mozilla Cryto Hash Function Component"
|
||||
#define NS_CRYPTO_HASH_CLASSNAME "Mozilla Crypto Hash Function Component"
|
||||
#define NS_CRYPTO_HASH_CID {0x36a1d3b3, 0xd886, 0x4317, {0x96, 0xff, 0x87, 0xb0, 0x00, 0x5c, 0xfe, 0xf7}}
|
||||
|
||||
#define NS_CRYPTO_HMAC_CLASSNAME "Mozilla Crypto HMAC Function Component"
|
||||
#define NS_CRYPTO_HMAC_CID {0xa496d0a2, 0xdff7, 0x4e23, {0xbd, 0x65, 0x1c, 0xa7, 0x42, 0xfa, 0x17, 0x8a}}
|
||||
|
||||
//--------------------------------------------
|
||||
// Now we need a content listener to register
|
||||
//--------------------------------------------
|
||||
|
@ -185,6 +189,20 @@ private:
|
|||
HASHContext* mHashContext;
|
||||
};
|
||||
|
||||
class nsCryptoHMAC : public nsICryptoHMAC
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSICRYPTOHMAC
|
||||
|
||||
nsCryptoHMAC();
|
||||
|
||||
private:
|
||||
~nsCryptoHMAC();
|
||||
|
||||
PK11Context* mHMACContext;
|
||||
};
|
||||
|
||||
struct PRLock;
|
||||
class nsNSSShutDownList;
|
||||
class nsSSLThread;
|
||||
|
|
|
@ -194,6 +194,7 @@ NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(PR_FALSE, nsCRLManager)
|
|||
NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(PR_FALSE, nsCipherInfoService)
|
||||
NS_NSS_GENERIC_FACTORY_CONSTRUCTOR_INIT(PR_FALSE, nsNTLMAuthModule, InitTest)
|
||||
NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(PR_FALSE, nsCryptoHash)
|
||||
NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(PR_FALSE, nsCryptoHMAC)
|
||||
NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(PR_FALSE, nsStreamCipher)
|
||||
NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(PR_FALSE, nsKeyObject)
|
||||
NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(PR_FALSE, nsKeyObjectFactory)
|
||||
|
@ -396,6 +397,13 @@ static const nsModuleComponentInfo components[] =
|
|||
nsCryptoHashConstructor
|
||||
},
|
||||
|
||||
{
|
||||
NS_CRYPTO_HMAC_CLASSNAME,
|
||||
NS_CRYPTO_HMAC_CID,
|
||||
NS_CRYPTO_HMAC_CONTRACTID,
|
||||
nsCryptoHMACConstructor
|
||||
},
|
||||
|
||||
{
|
||||
NS_CERT_PICKER_CLASSNAME,
|
||||
NS_CERT_PICKER_CID,
|
||||
|
|
|
@ -79,11 +79,32 @@ function doHash(algo, value, cmp) {
|
|||
}
|
||||
}
|
||||
|
||||
function doHashStream(algo, value, cmp) {
|
||||
var converter = new ScriptableUnicodeConverter();
|
||||
var hash = new CryptoHash(algo);
|
||||
|
||||
converter.charset = 'utf8';
|
||||
var stream = converter.convertToInputStream(value);
|
||||
hash.updateFromStream(stream, stream.available());
|
||||
hash = hexdigest(hash.finish(false));
|
||||
if (cmp != hash) {
|
||||
do_throw("Hash mismatch!\n" +
|
||||
" Expected: " + cmp + "\n" +
|
||||
" Actual: " + hash + "\n" +
|
||||
" Algo: " + algo);
|
||||
}
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
for (algo in hashes) {
|
||||
hashes[algo].forEach(
|
||||
function(e, i) {
|
||||
doHash(algo, messages[i], e);
|
||||
|
||||
if (messages[i].length) {
|
||||
// this test doesn't work for empty string/stream
|
||||
doHashStream(algo, messages[i], e);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
var ScriptableUnicodeConverter =
|
||||
Components.Constructor("@mozilla.org/intl/scriptableunicodeconverter",
|
||||
"nsIScriptableUnicodeConverter");
|
||||
|
||||
function getHMAC(data, key)
|
||||
{
|
||||
var converter = new ScriptableUnicodeConverter();
|
||||
converter.charset = 'utf8';
|
||||
var keyarray = converter.convertToByteArray(key, {});
|
||||
var dataarray = converter.convertToByteArray(data, {});
|
||||
|
||||
var cryptoHMAC = Components.classes["@mozilla.org/security/hmac;1"]
|
||||
.createInstance(Components.interfaces.nsICryptoHMAC);
|
||||
|
||||
cryptoHMAC.init(Components.interfaces.nsICryptoHMAC.SHA1, keyarray, keyarray.length);
|
||||
cryptoHMAC.update(dataarray, dataarray.length);
|
||||
var digest1 = cryptoHMAC.finish(true);
|
||||
|
||||
cryptoHMAC.reset();
|
||||
cryptoHMAC.update(dataarray, dataarray.length);
|
||||
var digest2 = cryptoHMAC.finish(true);
|
||||
|
||||
do_check_eq(digest1, digest2);
|
||||
|
||||
return digest1;
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
const key1 = 'MyKey_ABCDEFGHIJKLMN';
|
||||
const key2 = 'MyKey_01234567890123';
|
||||
|
||||
const dataA = "Secret message";
|
||||
const dataB = "Secres message";
|
||||
|
||||
var diegest1a = getHMAC(key1, dataA);
|
||||
var diegest2 = getHMAC(key2, dataA);
|
||||
var diegest1b = getHMAC(key1, dataA);
|
||||
|
||||
do_check_eq(diegest1a, diegest1b);
|
||||
do_check_neq(diegest1a, diegest2);
|
||||
|
||||
var diegest1 = getHMAC(key1, dataA);
|
||||
diegest2 = getHMAC(key1, dataB);
|
||||
|
||||
do_check_neq(diegest1, diegest2);
|
||||
}
|
Загрузка…
Ссылка в новой задаче