Esempio n. 1
0
def PBKDF_key_unwrap(key, encryptedKey, hashmod, dkLen, header={}):
    """
    Derive a key wrapping key using PBKDF2 and use it to unwrap
    a CEK using AES key wrap.

    @type  key: bytes
    @param key: The base key to be input to PBKDF2
    @type  encryptedKey: bytes
    @param encryptedKey: The wrapped CEK to be unwrapped
    @type  hashmod: module
    @param hashmod: Hash module to be used for PBKDF2
    @type  dkLen: int
    @param dkLen: Length of wrapping key to be derived, in octets
    @type  header: dict
    @param header: Header from which to pull salt, iteration count
    @rtype: bytes
    @return: The unwrapped CEK
    """

    # Pull salt and iterCount
    if "p2s" not in header:
        raise Exception("PBKDF2 requires 'p2s' parameter")
    if "p2c" not in header:
        raise Exception("PBKDF2 requires 'p2c' parameter")
    salt = b64dec(header["p2s"])
    iterCount = header["p2c"]

    # Compute the KEK and encrypt
    kek = PBKDF2(key, salt, iterCount, digestmodule=hashmod, \
        macmodule=HMAC).read(dkLen)
    return aes_key_unwrap(kek, encryptedKey)
Esempio n. 2
0
def PBKDF_key_wrap(key, CEK, hashmod, dkLen, header={}):
    """
    Derive a key wrapping key using PBKDF2 and use it to wrap
    a CEK using AES key wrap.

    Auto-generates salt and iteration count if not provided in header.

    @type  key: bytes
    @param key: The base key to be input to PBKDF2
    @type  CEK: bytes
    @param CEK: The CEK to be wrapped
    @type  hashmod: module
    @param hashmod: Hash module to be used for PBKDF2
    @type  dkLen: int
    @param dkLen: Length of wrapping key to be derived, in octets
    @type  header: dict
    @param header: Header from which to pull salt, iteration count
    @rtype: tuple
    @return: (encryptedKey, params), with encryptedKey as bytes, and
      params as dict containing salt and iteration count used
    """
    salt = iterCount = None
    # Pull or generate salt
    if "p2s" in header:
        salt = b64dec(header["p2s"])
    else:
        salt = Random.get_random_bytes(32)

    # Pull or generate iterCount
    if "p2c" in header:
        iterCount = b64dec(header["p2c"])
    else:
        iterCount = 2048

    # Compute the KEK and encrypt
    kek = PBKDF2(key, salt, iterCount, digestmodule=hashmod, \
        macmodule=HMAC).read(dkLen)
    encryptedKey = aes_key_wrap(kek, CEK)
    params = {"p2s": b64enc(salt), "p2c": iterCount}

    return (encryptedKey, params)
Esempio n. 3
0
def deserialize_JSON(x):
    """
    Deerialize a JOSE object from the JSON serialization

    @type  x: string
    @param x: A JSON-format JOSE object
    @rtype: dict
    @return: The unserialized form of the input
    """
    try:
        jx = json.loads(x)
    except:
        raise Exception("JSON-formatted object failed JSON parsing")

    # Hunt for base64url-encoded fields and decode them
    # XXX: Hunt within unprotected headers?  Probably not
    # Top level
    binary_fields = ["protected", "payload", "signature", "encrypted_key", \
        "iv", "ciphertext", "tag"]
    for name in binary_fields:
        if name in jx:
            jx[name] = b64dec(jx[name])

    # Second level for multi-recipient/signature
    if "signatures" in jx:
        for sig in jx["signatures"]:
            if "protected" in sig: sig["protected"] = b64dec(sig["protected"])
            sig["signature"] = b64dec(sig["signature"])
    if "recipients" in jx:
        for rcpt in jx["recipients"]:
            if "encrypted_key" in rcpt:
                rcpt["encrypted_key"] = b64dec(rcpt["encrypted_key"])

    if not isJOSE_unserialized(jx):
        raise Exception("Deserialized JOSE-JSON is not a JOSE object")
    return jx
Esempio n. 4
0
def deserialize_compact(x):
    """
    Deserialize a JOSE object from the compact serialization

    @type  x: string
    @param x: A compact-format JOSE object
    @rtype: dict
    @return: The unserialized form of the input
    """
    components = x.split(".")
    if len(components) == 2:
        jose = {
            "unprotected": json.loads(b64dec(components[0])),
            "payload": b64dec(components[1])
        }
    elif len(components) == 3:
        jose = {
            "protected": b64dec(components[0]),
            "payload": b64dec(components[1]),
            "signature": b64dec(components[2])
        }
    elif len(components) == 5:
        jose = {
            "protected": b64dec(components[0]),
            "ciphertext": b64dec(components[3]),
        }
        if len(components[1]) > 0:
            jose["encrypted_key"] = b64dec(components[1])
        if len(components[2]) > 0: jose["iv"] = b64dec(components[2])
        if len(components[4]) > 0: jose["tag"] = b64dec(components[4])
    else:
        raise Exception("Mal-formed compact object")

    if not isJOSE_unserialized(jose):
        raise Exception("Deserialized JOSE-compact is not a JOSE object")
    return jose
Esempio n. 5
0
def importKey(jwk, private=False):
    """
    Translate a JWK to one of the internal representations used by 
    our cryptographic libraries.
      - A symmetric key is a raw python byte string
      - An RSA public or private key is a PyCrypto RSA objects
      - An EC private key is a long
      - An EC public key is a tuple (long, long)

    @type  jwk: dict
    @param jwk: An unserialized JWK object
    @type  private: boolean
    @param private: Whether the imported key should be a private key (public default)
    @rtype: any
    @return: A native key object
    """
    kty = getOrRaise(jwk, "kty")
    if kty == "oct":
        return b64dec(getOrRaise(jwk, "k"))
    elif kty == "RSA":
        n = bytes_to_long(b64dec(getOrRaise(jwk, "n")))
        e = bytes_to_long(b64dec(getOrRaise(jwk, "e")))
        if not private:
            return RSA.construct((n,e))
        else:
            d = bytes_to_long(b64dec(getOrRaise(jwk, "d")))
            return RSA.construct((n,e,d))
    elif kty == "EC":
        if not private:
            x = bytes_to_long(b64dec(getOrRaise(jwk, "x")))
            y = bytes_to_long(b64dec(getOrRaise(jwk, "y")))
            return (x, y)
        else:
            return bytes_to_long(b64dec(getOrRaise(jwk, "d")))
    else: 
        raise Exception("Unknown key type {}".format(jwk["kty"])) 
Esempio n. 6
0
def decryptKey(alg, enc, jwk, encryptedKey, header={}):
    """
    Decrypt a JWE encrypted key.
    
    @type  alg: string
    @param alg: The JWE "alg" value specifying the key management algorithm
    @type  enc: string
    @param enc: The JWE "enc" value specifying the encryption algorithm
    @type  jwk: dict
    @param jwk: The key to be used, as an unserialized JWK object
    @type  encryptedKey: bytes
    @param encryptedKey: The key to decrypt
    @type  header: dict
    @param header: A header object containing additional parameters
    @rtype: bytes
    @return: The decrypted key
    """
    key = importKey(jwk, private=True)
    if alg == "RSA1_5":
        sentinel = "fnord"
        cipher = PKCS1_v1_5.new(key)
        CEK = cipher.decrypt(encryptedKey, sentinel)
        if CEK == sentinel:
            raise Exception("Unable to unwrap key")
        return CEK
    elif alg == "RSA-OAEP":
        cipher = PKCS1_OAEP.new(key)
        CEK = cipher.decrypt(encryptedKey)
        return CEK
    elif alg in ["A128KW", "A192KW", "A256KW"]:
        return polyfills.aes_key_unwrap(key, encryptedKey)
    elif alg == "dir":
        if encryptedKey and len(encryptedKey) > 0:
            raise Exception("Direct encryption with non-empty encrypted key")
        return key
    elif alg in ["ECDH-ES", "ECDH-ES+A128KW", "ECDH-ES+A192KW", "ECDH-ES+A256KW"]:
        # Pull the input parameters 
        epk = importKey(getOrRaise(header, "epk"))
        apu = b64dec(getOrRaise(header, "apu"))
        apv = b64dec(getOrRaise(header, "apv"))
        # Select the curve
        curve = NISTEllipticCurve.byName(getOrRaise(header["epk"], "crv"))
        # Derive the KEK and decrypt
        if alg == "ECDH-ES":
            dkLen = keyLength(enc)
            return polyfills.ECDH_deriveKey(curve, key, epk, apu, apv, enc, dkLen)
        elif alg == "ECDH-ES+A128KW":
            kek = polyfills.ECDH_deriveKey(curve, key, epk, apu, apv, "A128KW", 128)
            return polyfills.aes_key_unwrap(kek, encryptedKey)
        elif alg == "ECDH-ES+A192KW":
            kek = polyfills.ECDH_deriveKey(curve, key, epk, apu, apv, "A192KW", 192)
            return polyfills.aes_key_unwrap(kek, encryptedKey)
        elif alg == "ECDH-ES+A256KW":
            kek = polyfills.ECDH_deriveKey(curve, key, epk, apu, apv, "A256KW", 256)
            return polyfills.aes_key_unwrap(kek, encryptedKey)
    elif alg in ["A128GCMKW", "A192GCMKW", "A256GCMKW"]:
        iv = b64dec(getOrRaise(header, "iv"))
        tag = b64dec(getOrRaise(header, "tag"))
        gcm = AES_GCM(bytes_to_long(key))
        return gcm.decrypt(bytes_to_long(iv), encryptedKey, bytes_to_long(tag), '')
    elif alg == "PBES2-HS256+A128KW":
        return polyfills.PBKDF_key_unwrap(key, encryptedKey, SHA256, 16, header)
    elif alg == "PBES2-HS384+A192KW":
        return polyfills.PBKDF_key_unwrap(key, encryptedKey, SHA384, 24, header)
    elif alg == "PBES2-HS512+A256KW":
        return polyfills.PBKDF_key_unwrap(key, encryptedKey, SHA512, 32, header)
    else: 
        raise Exception("Unsupported key management algorithm " + alg)
Esempio n. 7
0
def generateSenderParams(alg, enc, jwk, header={}, inCEK=None):
    """
    Generate parameters for the sender of a JWE.  This is essentially an 
    encryptKey method, except (1) in some cases, the key is specified directly
    or derived ("dir", "ECDH"), and (2) other parameters besides the encrypted
    key are generated (e.g., the IV).

    This method returns several things:
      - A random CEK (can be overridden with the inCEK parameter)
      - The encrypted CEK
      - A random IV
      - A dictionary of parameters generated within this function

    The idea is that the parameters generated within this function (e.g.,
    "epk", "p2s") should be added back to the JWE header

    @type  alg: string
    @param alg: The JWE "alg" value specifying the key management algorithm
    @type  enc: string
    @param enc: The JWE "enc" value specifying the encryption algorithm
    @type  jwk: dict
    @param jwk: The key to be used, as an unserialized JWK object
    @type  header: dict
    @param header: A header object with additional parameters
    @type  inCEK: bytes
    @param inCEK: A fixed CEK (overrides random CEK generation)
    @rtype: tuple
    @return: (CEK, encryptedKey, IV, params), the first three as bytes, 
      params as dict.  
    """
    # Generate a random key/iv for enc
    (CEK, IV) = generateKeyIV(enc)
    if inCEK:
        CEK = inCEK
    encryptedKey = ""
    params = {}
    key = importKey(jwk, private=False)

    # Encrypt key / generate params as defined by alg
    if alg == "RSA1_5":
        cipher = PKCS1_v1_5.new(key)
        encryptedKey = cipher.encrypt(CEK)
    elif alg == "RSA-OAEP":
        (CEK, IV) = generateKeyIV(enc)
        cipher = PKCS1_OAEP.new(key)
        encryptedKey = cipher.encrypt(CEK)
    elif alg in ["A128KW", "A192KW", "A256KW"]:
        encryptedKey = polyfills.aes_key_wrap(key, CEK)
    elif alg == "dir":
        CEK = key
    elif alg in ["ECDH-ES", "ECDH-ES+A128KW", "ECDH-ES+A192KW", "ECDH-ES+A256KW"]:
        # Generate the input parameters 
        apu = b64dec(header["apu"]) if "apu" in header else Random.get_random_bytes(16) 
        apv = b64dec(header["apv"]) if "apv" in header else Random.get_random_bytes(16) 
        # Generate an ephemeral key pair
        curve = NISTEllipticCurve.byName(getOrRaise(jwk, "crv"))
        if "epk" in header:
            epk = importKey(header["epk"], private=False)
            eprivk = importKey(header["epk"], private=True)
        else:
            (eprivk, epk) = curve.keyPair()
        # Derive the KEK and encrypt
        params = {
            "apu": b64enc(apu),
            "apv": b64enc(apv),
            "epk": exportKey(epk, "EC", curve)
        }
        if alg == "ECDH-ES":
            dkLen = keyLength(enc)
            CEK = polyfills.ECDH_deriveKey(curve, eprivk, key, apu, apv, enc, dkLen)
        elif alg == "ECDH-ES+A128KW":
            kek = polyfills.ECDH_deriveKey(curve, eprivk, key, apu, apv, "A128KW", 128)
            encryptedKey = polyfills.aes_key_wrap(kek, CEK)
        elif alg == "ECDH-ES+A192KW":
            kek = polyfills.ECDH_deriveKey(curve, eprivk, key, apu, apv, "A192KW", 192)
            encryptedKey = polyfills.aes_key_wrap(kek, CEK)
        elif alg == "ECDH-ES+A256KW":
            kek = polyfills.ECDH_deriveKey(curve, eprivk, key, apu, apv, "A256KW", 256)
            encryptedKey = polyfills.aes_key_wrap(kek, CEK)
    elif alg in ["A128GCMKW", "A192GCMKW", "A256GCMKW"]:
        iv = Random.get_random_bytes(12)
        gcm = AES_GCM(bytes_to_long(key))
        encryptedKey, tag = gcm.encrypt(bytes_to_long(iv), CEK, '')
        params = { "iv": b64enc(iv), "tag": b64enc(long_to_bytes(tag,16)) }
    elif alg == "PBES2-HS256+A128KW":
        (CEK, IV) = generateKeyIV(enc)
        (encryptedKey, params) = \
            polyfills.PBKDF_key_wrap( key, CEK, SHA256, 16, header )
    elif alg == "PBES2-HS384+A192KW":
        (CEK, IV) = generateKeyIV(enc)
        (encryptedKey, params) = \
            polyfills.PBKDF_key_wrap( key, CEK, SHA384, 24, header )
    elif alg == "PBES2-HS512+A256KW":
        (CEK, IV) = generateKeyIV(enc)
        (encryptedKey, params) = \
            polyfills.PBKDF_key_wrap( key, CEK, SHA512, 32, header )
    else: 
        raise Exception("Unsupported key management algorithm " + alg)
    
    return (CEK, encryptedKey, IV, params)