def test_authtoken_rs256_verification(self): # Verify that the auth token (signed by Bouncer's private key) # can be validated using Bouncer's public key. Obtain/construct # Bouncer's public key from it's JSON Web Key Set (jwks) entpoint # Obtain authentication token. token, _ = self.test_authtoken_rs256_anatomy() # Obtain the JSON Web Key Set. r = requests.get(Url('/auth/jwks')) keys = r.json()['keys'][0] # Extract the public modulus and exponent from the data. exponent_bytes = base64url_decode(keys['e'].encode('ascii')) exponent_int = bytes_to_number(exponent_bytes) modulus_bytes = base64url_decode(keys['n'].encode('ascii')) modulus_int = bytes_to_number(modulus_bytes) # Generate a public key instance from these numbers. public_numbers = rsa.RSAPublicNumbers(n=modulus_int, e=exponent_int) public_key = public_numbers.public_key(backend=cryptography_backend) # Verify token signature using that public key. payload = jwt.decode(token, public_key, algorithms='RS256') assert payload['uid'] == self.user1_uid
def get_key(self, kid): keys = self.get_keys().get('keys') key = list(filter(lambda x: x.get('kid') == kid, keys))[0] _e = key['e'] _n = key['n'] e = int.from_bytes(base64url_decode(_e), 'big') n = int.from_bytes(base64url_decode(_n), 'big') return RSAPublicNumbers(e, n).public_key(default_backend())
def _payload(self): if self._signatures is None: return self._bytes.as_encoded_str() byte_data = self._bytes.as_encoded_str() protected = str(self._signatures[0][DOCKER_SCHEMA1_PROTECTED_KEY]) parsed_protected = json.loads(base64url_decode(protected)) signed_content_head = byte_data[:parsed_protected[ DOCKER_SCHEMA1_FORMAT_LENGTH_KEY]] signed_content_tail = base64url_decode( str(parsed_protected[DOCKER_SCHEMA1_FORMAT_TAIL_KEY])) return signed_content_head + signed_content_tail
def test_ec_verify_should_return_true_for_test_vector(self): """ This test verifies that ECDSA verification works with a known good signature and key. Reference: https://tools.ietf.org/html/rfc7520#section-4.3 """ signing_input = ensure_bytes( 'eyJhbGciOiJFUzUxMiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZXhhb' 'XBsZSJ9.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb' '3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdS' 'Bkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmU' 'geW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4') signature = base64url_decode( ensure_bytes( 'AE_R_YZCChjn4791jSQCrdPZCNYqHXCTZH0-JZGYNlaAjP2kqaluUIIUnC9qvbu9P' 'lon7KRTzoNEuT4Va2cmL1eJAQy3mtPBu_u_sDDyYjnAMDxXPn7XrT0lw-kvAD890j' 'l8e2puQens_IEKBpHABlsbEPX6sFY8OcGDqoRuBomu9xQ2')) algo = ECAlgorithm(ECAlgorithm.SHA512) key = algo.prepare_key(load_ec_pub_key()) result = algo.verify(signing_input, key, signature) assert result
def user_data(self, access_token, *args, **kwargs): response = kwargs.get('response') id_token = response.get('id_token') if six.PY2: # str() to fix a bug in Python's base64 # https://stackoverflow.com/a/2230623/161278 id_token = str(id_token) jwt_header_json = base64url_decode(id_token.split('.')[0]) jwt_header = json.loads(jwt_header_json.decode('ascii')) # `kid` is short for key id key = self.get_public_key(jwt_header['kid']) try: return jwt_decode( id_token, key=key, algorithms=jwt_header['alg'], audience=self.setting('KEY'), leeway=self.setting('JWT_LEEWAY', default=0), ) except (DecodeError, ExpiredSignature) as error: raise AuthTokenError(self, error)
def test_rsapss_verify_should_return_true_for_test_vector(self): """ This test verifies that RSA-PSS verification works with a known good signature and key. Reference: https://tools.ietf.org/html/rfc7520#section-4.2 """ signing_input = ensure_bytes( 'eyJhbGciOiJQUzM4NCIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZXhhb' 'XBsZSJ9.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb' '3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdS' 'Bkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmU' 'geW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4') signature = base64url_decode( ensure_bytes( 'cu22eBqkYDKgIlTpzDXGvaFfz6WGoz7fUDcfT0kkOy42miAh2qyBzk1xEsnk2IpN6' '-tPid6VrklHkqsGqDqHCdP6O8TTB5dDDItllVo6_1OLPpcbUrhiUSMxbbXUvdvWXz' 'g-UD8biiReQFlfz28zGWVsdiNAUf8ZnyPEgVFn442ZdNqiVJRmBqrYRXe8P_ijQ7p' '8Vdz0TTrxUeT3lm8d9shnr2lfJT8ImUjvAA2Xez2Mlp8cBE5awDzT0qI0n6uiP1aC' 'N_2_jLAeQTlqRHtfa64QQSUmFAAjVKPbByi7xho0uTOcbH510a6GYmJUAfmWjwZ6o' 'D4ifKo8DYM-X72Eaw')) algo = RSAPSSAlgorithm(RSAPSSAlgorithm.SHA384) key = algo.prepare_key(load_rsa_pub_key()) result = algo.verify(signing_input, key, signature) assert result
def test_request_authtoken_without_expiration(self): # Include the `exp` key in the `data` dictionary, with the value 0. # Note(JP): this is a private DC/OS interface introduced as a workaround # for the problem of reliable authentication token refresh. It must # never be publicly documented, and should be removed in a future # version. login_token = jwt.encode( { 'exp': int(time.time() + 5 * 60), 'uid': self.service1_uid }, self.service1_private_key, algorithm='RS256').decode('ascii') r = requests.post(url=Url('/auth/login'), json={ 'exp': 0, 'uid': self.service1_uid, 'token': login_token }) assert r.status_code == 200 token = r.json()['token'] _, payload_bytes, _ = [ base64url_decode(_.encode('ascii')) for _ in token.split(".") ] payload_dict = json.loads(payload_bytes.decode('ascii')) assert 'exp' not in payload_dict
def test_rsa_verify_should_return_true_for_test_vector(self): """ This test verifies that RSA PKCS v1.5 verification works with a known good signature and key. Reference: https://tools.ietf.org/html/rfc7520#section-4.1 """ signing_input = ensure_bytes( 'eyJhbGciOiJSUzI1NiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZXhhb' 'XBsZSJ9.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb' '3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdS' 'Bkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmU' 'geW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4') signature = base64url_decode( ensure_bytes( 'MRjdkly7_-oTPTS3AXP41iQIGKa80A0ZmTuV5MEaHoxnW2e5CZ5NlKtainoFmKZop' 'dHM1O2U4mwzJdQx996ivp83xuglII7PNDi84wnB-BDkoBwA78185hX-Es4JIwmDLJ' 'K3lfWRa-XtL0RnltuYv746iYTh_qHRD68BNt1uSNCrUCTJDt5aAE6x8wW1Kt9eRo4' 'QPocSadnHXFxnt8Is9UzpERV0ePPQdLuW3IS_de3xyIrDaLGdjluPxUAhb6L2aXic' '1U12podGU0KLUQSE_oI-ZnmKJ3F4uOZDnd6QZWJushZ41Axf_fcIe8u9ipH84ogor' 'ee7vjbU5y18kDquDg')) algo = RSAAlgorithm(RSAAlgorithm.SHA256) key = algo.prepare_key(load_rsa_pub_key()) result = algo.verify(signing_input, key, signature) assert result
def test_rsa_verify_should_return_true_for_test_vector(self): """ This test verifies that RSA PKCS v1.5 verification works with a known good signature and key. Reference: https://tools.ietf.org/html/rfc7520#section-4.1 """ signing_input = ensure_bytes( 'eyJhbGciOiJSUzI1NiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZXhhb' 'XBsZSJ9.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb' '3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdS' 'Bkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmU' 'geW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4' ) signature = base64url_decode(ensure_bytes( 'MRjdkly7_-oTPTS3AXP41iQIGKa80A0ZmTuV5MEaHoxnW2e5CZ5NlKtainoFmKZop' 'dHM1O2U4mwzJdQx996ivp83xuglII7PNDi84wnB-BDkoBwA78185hX-Es4JIwmDLJ' 'K3lfWRa-XtL0RnltuYv746iYTh_qHRD68BNt1uSNCrUCTJDt5aAE6x8wW1Kt9eRo4' 'QPocSadnHXFxnt8Is9UzpERV0ePPQdLuW3IS_de3xyIrDaLGdjluPxUAhb6L2aXic' '1U12podGU0KLUQSE_oI-ZnmKJ3F4uOZDnd6QZWJushZ41Axf_fcIe8u9ipH84ogor' 'ee7vjbU5y18kDquDg' )) algo = RSAAlgorithm(RSAAlgorithm.SHA256) key = algo.prepare_key(load_rsa_pub_key()) result = algo.verify(signing_input, key, signature) assert result
def test_request_authtoken_with_custom_expiration(self): # Include the `exp` key in the `data` dictionary, with a non-zero # integer value. Note(JP): this is a private DC/OS interface introduced # as a workaround for the problem of reliable authentication token # refresh. It must never be publicly documented, and should be removed # in a future version, together with this test. url = Url('/auth/login') data = { 'uid': self.service1_uid, 'exp': 84600, 'token': jwt.encode( { 'exp': int(time.time() + 5 * 60), 'uid': self.service1_uid }, self.service1_private_key, algorithm='RS256').decode('ascii') } r = requests.post(url, json=data) assert r.status_code == 200 token = r.json()['token'] _, payload_bytes, _ = [ base64url_decode(_.encode('ascii')) for _ in token.split(".") ] payload_dict = json.loads(payload_bytes.decode('ascii')) assert payload_dict['exp'] == 84600
def test_ec_verify_should_return_true_for_test_vector(self): """ This test verifies that ECDSA verification works with a known good signature and key. Reference: https://tools.ietf.org/html/rfc7520#section-4.3 """ signing_input = ensure_bytes( 'eyJhbGciOiJFUzUxMiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZXhhb' 'XBsZSJ9.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb' '3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdS' 'Bkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmU' 'geW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4' ) signature = base64url_decode(ensure_bytes( 'AE_R_YZCChjn4791jSQCrdPZCNYqHXCTZH0-JZGYNlaAjP2kqaluUIIUnC9qvbu9P' 'lon7KRTzoNEuT4Va2cmL1eJAQy3mtPBu_u_sDDyYjnAMDxXPn7XrT0lw-kvAD890j' 'l8e2puQens_IEKBpHABlsbEPX6sFY8OcGDqoRuBomu9xQ2' )) algo = ECAlgorithm(ECAlgorithm.SHA512) key = algo.prepare_key(load_ec_pub_key()) result = algo.verify(signing_input, key, signature) assert result
def test_rsapss_verify_should_return_true_for_test_vector(self): """ This test verifies that RSA-PSS verification works with a known good signature and key. Reference: https://tools.ietf.org/html/rfc7520#section-4.2 """ signing_input = ensure_bytes( 'eyJhbGciOiJQUzM4NCIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZXhhb' 'XBsZSJ9.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb' '3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdS' 'Bkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmU' 'geW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4' ) signature = base64url_decode(ensure_bytes( 'cu22eBqkYDKgIlTpzDXGvaFfz6WGoz7fUDcfT0kkOy42miAh2qyBzk1xEsnk2IpN6' '-tPid6VrklHkqsGqDqHCdP6O8TTB5dDDItllVo6_1OLPpcbUrhiUSMxbbXUvdvWXz' 'g-UD8biiReQFlfz28zGWVsdiNAUf8ZnyPEgVFn442ZdNqiVJRmBqrYRXe8P_ijQ7p' '8Vdz0TTrxUeT3lm8d9shnr2lfJT8ImUjvAA2Xez2Mlp8cBE5awDzT0qI0n6uiP1aC' 'N_2_jLAeQTlqRHtfa64QQSUmFAAjVKPbByi7xho0uTOcbH510a6GYmJUAfmWjwZ6o' 'D4ifKo8DYM-X72Eaw' )) algo = RSAPSSAlgorithm(RSAPSSAlgorithm.SHA384) key = algo.prepare_key(load_rsa_pub_key()) result = algo.verify(signing_input, key, signature) assert result
def from_base64url_uint(val): if isinstance(val, str): val = val.encode('ascii') data = base64url_decode(val) buf = struct.unpack('%sB' % len(data), data) return int(''.join(["%02x" % byte for byte in buf]), 16)
def test_service_account_create_login_delete(dcos_api_session, noauth_api_session): # Create service user account, share the public key with the IAM. serviceuid = 'testservice' r = dcos_api_session.put('/acs/api/v1/users/' + serviceuid, json={ 'description': 'foo', 'public_key': default_rsa_pubkey }) assert r.status_code == 201, r.text # Generate short-lived service login token (RS256 JWT signed with # the service's private key). service_login_token = jwt.encode( { 'uid': serviceuid, 'exp': time.time() + 30 }, default_rsa_privkey, algorithm='RS256').decode('ascii') # Log in via the service login token. r = noauth_api_session.post('/acs/api/v1/auth/login', json={ 'uid': serviceuid, 'token': service_login_token }) assert r.status_code == 200, r.text # Confirm that the response body contains a DC/OS authentication token. token = r.json()['token'] header_bytes, payload_bytes, signature_bytes = [ base64url_decode(_.encode('ascii')) for _ in token.split(".") ] header_dict = json.loads(header_bytes.decode('ascii')) assert header_dict['alg'] == 'RS256' assert header_dict['typ'] == 'JWT' payload_dict = json.loads(payload_bytes.decode('ascii')) assert 'exp' in payload_dict assert 'uid' in payload_dict assert payload_dict['uid'] == serviceuid # Verify that the service user account appears in the users collection. r = dcos_api_session.get('/acs/api/v1/users', query='type=service') uids = [o['uid'] for o in r.json()['array']] assert serviceuid in uids # Delete the service user account. r = dcos_api_session.delete('/acs/api/v1/users/' + serviceuid) assert r.status_code == 204 # Confirm that service does not appear in collection anymore. r = dcos_api_session.get('/acs/api/v1/users', query='type=service') uids = [o['uid'] for o in r.json()['array']] assert serviceuid not in uids
def verify_jwt(token, jwks): hmac_key = get_hmac_key(token, jwks) if not hmac_key: raise ValueError("No public key found") hmac_key = jwk.construct(get_hmac_key(token, jwks)) message, encoded_signature = token.rsplit(".", 1) decoded_signature = base64url_decode(encoded_signature.encode()) return hmac_key.verify(message.encode(), decoded_signature)
def _get_type(self): raw_header = self.raw_jwt.split(".")[0] header: Dict[str, str] = json.loads(base64url_decode(raw_header)) if header.get("class") not in ("access", "refresh"): raise JWTDecodeError( "Can not resolve token type by JOSE header. missing 'class'") return header.get("class")
def verify_token(masterUrl, authToken): keys = requests.get('https://' + masterUrl + '/acs/api/v1/auth/jwks', verify=False).json()['keys'][0] exponent_bytes = base64url_decode(keys['e'].encode('ascii')) exponent_int = bytes_to_number(exponent_bytes) modulus_bytes = base64url_decode(keys['n'].encode('ascii')) modulus_int = bytes_to_number(modulus_bytes) public_numbers = rsa.RSAPublicNumbers(n=modulus_int, e=exponent_int) public_key = public_numbers.public_key(backend=default_backend()) payload = jwt.decode(authToken, public_key, algorithm='RS256') print payload print "expiration is: " + time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(payload['exp']))
def verify_jwk_token(self, jwt_credentials): try: public_key = self.kid_to_jwk[jwt_credentials.header['kid']] except KeyError: raise HTTPException(status_code=HTTP_401_UNAUTHORIZED, detail="JWT public key not found") key = jwk.construct(public_key) decoded_signature = base64url_decode( jwt_credentials.signature.encode()) return key.verify(jwt_credentials.message.encode(), decoded_signature)
def jwt2john(jwt): """ Convert signature from base64 to hex, and separate it from the data by a # so that John can parse it. """ jwt_bytes = jwt.encode('ascii') parts = jwt_bytes.split(b'.') data = parts[0] + b'.' + parts[1] signature = hexlify(base64url_decode(parts[2])) return (data + b'#' + signature).decode('ascii')
def test_service_account_create_login_delete( dcos_api_session, noauth_api_session): # Create service user account, share the public key with the IAM. serviceuid = 'testservice' r = dcos_api_session.put( '/acs/api/v1/users/' + serviceuid, json={'description': 'foo', 'public_key': default_rsa_pubkey} ) assert r.status_code == 201, r.text # Generate short-lived service login token (RS256 JWT signed with # the service's private key). service_login_token = jwt.encode( {'uid': serviceuid, 'exp': time.time() + 30}, default_rsa_privkey, algorithm='RS256' ).decode('ascii') # Log in via the service login token. r = noauth_api_session.post( '/acs/api/v1/auth/login', json={'uid': serviceuid, 'token': service_login_token} ) assert r.status_code == 200, r.text # Confirm that the response body contains a DC/OS authentication token. token = r.json()['token'] header_bytes, payload_bytes, signature_bytes = [ base64url_decode(_.encode('ascii')) for _ in token.split(".")] header_dict = json.loads(header_bytes.decode('ascii')) assert header_dict['alg'] == 'RS256' assert header_dict['typ'] == 'JWT' payload_dict = json.loads(payload_bytes.decode('ascii')) assert 'exp' in payload_dict assert 'uid' in payload_dict assert payload_dict['uid'] == serviceuid # Verify that the service user account appears in the users collection. r = dcos_api_session.get('/acs/api/v1/users', query='type=service') uids = [o['uid'] for o in r.json()['array']] assert serviceuid in uids # Delete the service user account. r = dcos_api_session.delete('/acs/api/v1/users/' + serviceuid) assert r.status_code == 204 # Confirm that service does not appear in collection anymore. r = dcos_api_session.get('/acs/api/v1/users', query='type=service') uids = [o['uid'] for o in r.json()['array']] assert serviceuid not in uids
def jwt2john(jwt): """ Converts the signature from base64 to hex, and seperates it from the data with a # allowing John to parse it. :param jwt: JWT string to johnify :return: johnified JWT string """ parts = jwt.split('.') data = parts[0] + '.' + parts[1] signature = hexlify(base64url_decode(parts[2])) return f"{data}#{signature.decode('utf-8')}"
def _user_is_admin(user_token): """ :param str user_token: User's OAuth 2 token payload (containing "bearer" prefix). :return: True if the user is an admin. :rtype: bool """ # get token without "bearer" token = user_token.split()[1] # take the middle part with payload token_payload_based = token.split('.')[1] token_payload_str = base64url_decode(token_payload_based.encode()).decode() token_payload = json.loads(token_payload_str) return 'console.admin' in token_payload['scope']
def jwt_token_verify(id_token, client_id, issuer, autoconfig=None, jwk=None): """ Verify a JWT token using public key. If jwk is not provided the issuer must support auto-discovery. This will also slow down the login process since multiple remote calls are required to fetch jwk. :param id_token: The openid id_token returned by the authorisation call :param client_id: The client_id, required for JWT verification :param issuer: The issuer, required for JWT verification and for auto-configuration if necessary :param autoconfig: Dictionary of auto-configuration properties, if empty will be fetched if required :param jwk: The JSON web key, if empty will be fetched using autoconfig :return dict: The decoded verified token :raises Exception: If verification failed """ # https://pyjwt.readthedocs.io/en/latest/usage.html # https://openid.net/specs/openid-connect-core-1_0.html#IDToken if not jwk: header = jwt.get_unverified_header(id_token) if not autoconfig: autoconfig = openid_connect_discover(issuer) jwks = _cache_get(autoconfig['jwks_uri']) for jwk in jwks['keys']: if jwk['kid'] == header['kid']: break if not jwk: raise Exception('Failed to get public key for {}'.format(issuer)) e = int(codecs.encode(base64url_decode(jwk['e']), 'hex'), 16) n = int(codecs.encode(base64url_decode(jwk['n']), 'hex'), 16) public_key = RSAPublicNumbers(e, n).public_key(backend=default_backend()) pem = public_key.public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo) d = jwt.decode(id_token, key=pem, algorithms=jwk['alg'], audience=client_id, issuer=issuer) return d
def _unpack(cls, token): if isinstance(token, (str, unicode)): token = token.encode('utf-8') try: signing_input, crypto_segment = token.rsplit(b'.', 1) header_segment, payload_segment = signing_input.split(b'.', 1) except ValueError: raise DecodeError('Not enough segments') try: header_data = base64url_decode(header_segment) except (TypeError, binascii.Error): raise DecodeError('Invalid header padding') try: header = json.loads(header_data.decode('utf-8')) except ValueError as e: raise DecodeError('Invalid header string: %s' % e) if not isinstance(header, Mapping): raise DecodeError('Invalid header string: must be a json object') try: payload_data = base64url_decode(payload_segment) except (TypeError, binascii.Error): raise DecodeError('Invalid payload padding') try: payload = json.loads(payload_data.decode('utf-8')) except ValueError as e: raise DecodeError('Invalid payload string: %s' % e) try: signature = base64url_decode(crypto_segment) except (TypeError, binascii.Error): raise DecodeError('Invalid crypto padding') return (header, payload, signature, signing_input)
def dcos_adminrouter(b, opts): b.cluster_id() # Require the IAM to already be up and running. The IAM contains logic for # achieving consensus about a key pair, and exposes the public key # information via its JWKS endpoint. Talk directly to the local IAM instance # which is reachable via the local network interface. r = requests.get('http://127.0.0.1:8101/acs/api/v1/auth/jwks') if r.status_code != 200: log.info('JWKS retrieval failed. Got %s with body: %s', r, r.text) sys.exit(1) jwks = r.json() # The first key in the JSON Web Key Set corresponds to the current private # key used for signing authentiction tokens. key = jwks['keys'][0] exponent_bytes = base64url_decode(key['e'].encode('ascii')) exponent_int = bytes_to_number(exponent_bytes) modulus_bytes = base64url_decode(key['n'].encode('ascii')) modulus_int = bytes_to_number(modulus_bytes) # Generate a `cryptography` public key object instance from these numbers. public_numbers = rsa.RSAPublicNumbers(n=modulus_int, e=exponent_int) public_key = public_numbers.public_key( backend=cryptography.hazmat.backends.default_backend()) # Serialize public key into the OpenSSL PEM public key format RFC 5280). pubkey_pem_bytes = public_key.public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo) rundir = utils.dcos_run_path / 'dcos-adminrouter' rundir.mkdir(parents=True, exist_ok=True) pubkey_path = rundir / 'auth-token-verification-key' utils.write_public_file(pubkey_path, pubkey_pem_bytes) utils.chown(pubkey_path, user='******')
def get_by_token(self, token) -> typing.Union['Participant', None]: if token is None or len(token) == 0: return None try: token = base64url_decode(token) participant_id = signer.unsign(str(token, "utf-8"), max_age=86400 * 7) except BadSignature: return None except binascii.Error: return None participant, _ = Participant.objects.get_or_create(pk=participant_id) return participant
def is_admin(user_token): """ :param str user_token: User's OAuth 2 token payload (containing "bearer" prefix). :return: True if the user is an admin. :rtype: bool """ print('Provided token:', user_token) # get token without "bearer" token = user_token.split()[1] # take the middle part with payload token_payload_based = token.split('.')[1] token_payload_str = base64url_decode(token_payload_based.encode()).decode() token_payload = json.loads(token_payload_str) return 'console.admin' in token_payload['scope']
def dcos_adminrouter(b, opts): b.cluster_id() # Require the IAM to already be up and running. The IAM contains logic for # achieving consensus about a key pair, and exposes the public key # information via its JWKS endpoint. Talk directly to the local IAM instance # which is reachable via the local network interface. r = requests.get('http://127.0.0.1:8101/acs/api/v1/auth/jwks') if r.status_code != 200: log.info('JWKS retrieval failed. Got %s with body: %s', r, r.text) sys.exit(1) jwks = r.json() # The first key in the JSON Web Key Set corresponds to the current private # key used for signing authentiction tokens. key = jwks['keys'][0] exponent_bytes = base64url_decode(key['e'].encode('ascii')) exponent_int = bytes_to_number(exponent_bytes) modulus_bytes = base64url_decode(key['n'].encode('ascii')) modulus_int = bytes_to_number(modulus_bytes) # Generate a `cryptography` public key object instance from these numbers. public_numbers = rsa.RSAPublicNumbers(n=modulus_int, e=exponent_int) public_key = public_numbers.public_key( backend=cryptography.hazmat.backends.default_backend()) # Serialize public key into the OpenSSL PEM public key format RFC 5280). pubkey_pem_bytes = public_key.public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo) os.makedirs('/run/dcos/dcos-adminrouter', exist_ok=True) pubkey_path = '/run/dcos/dcos-adminrouter/auth-token-verification-key' _write_file_bytes(pubkey_path, pubkey_pem_bytes, 0o644) shutil.chown(pubkey_path, user='******')
def test_authtoken_rs256_anatomy(self): token = self.test_privkey_login_succeeds() header_bytes, payload_bytes, signature_bytes = [ base64url_decode(_.encode('ascii')) for _ in token.split(".") ] assert b'typ' in header_bytes header_dict = json.loads(header_bytes.decode('ascii')) assert header_dict['alg'] == "RS256" assert header_dict['typ'] == "JWT" payload_dict = json.loads(payload_bytes.decode('ascii')) assert 'exp' in payload_dict assert 'uid' in payload_dict assert payload_dict['uid'] == self.service1_uid
def test_encode_headers_parameter_adds_headers(self, jws, payload): headers = {'testheader': True} token = jws.encode(payload, 'secret', headers=headers) if not isinstance(token, string_types): token = token.decode() header = token[0:token.index('.')].encode() header = base64url_decode(header) if not isinstance(header, text_type): header = header.decode() header_obj = json.loads(header) assert 'testheader' in header_obj assert header_obj['testheader'] == headers['testheader']
def test_encode_headers_parameter_adds_headers(self, jws, payload): headers = {"testheader": True} token = jws.encode(payload, "secret", headers=headers) if not isinstance(token, string_types): token = token.decode() header = token[0:token.index(".")].encode() header = base64url_decode(header) if not isinstance(header, text_type): header = header.decode() header_obj = json.loads(header) assert "testheader" in header_obj assert header_obj["testheader"] == headers["testheader"]
def test_hmac_verify_should_return_true_for_test_vector(self): signing_input = ensure_bytes( 'eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LWVlZ' 'jMxNGJjNzAzNyJ9.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ' '29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIG' 'lmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmc' 'gd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4' ) signature = base64url_decode(ensure_bytes( 's0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0' )) algo = HMACAlgorithm(HMACAlgorithm.SHA256) key = algo.prepare_key(load_hmac_key()) result = algo.verify(signing_input, key, signature) assert result
def forged_user_header(jwt_generator): """Return JWT token with a forged UID claim""" token = jwt_generator(uid='bozydar') # Decode token: header_bytes, payload_bytes, signature_bytes = [ base64url_decode(_.encode('ascii')) for _ in token.split(".")] payload_dict = json.loads(payload_bytes.decode('ascii')) # Rewrite uid and invert token decode procedure. payload_dict['uid'] = 'fafok' payload_bytes = json.dumps(payload_dict).encode('utf-8') forged_token = '.'.join( base64url_encode(_).decode('ascii') for _ in ( header_bytes, payload_bytes, signature_bytes) ) header = {'Authorization': 'token={}'.format(forged_token)} return header
def forged_user_header(jwt_generator): """Return JWT token with a forged UID claim""" token = jwt_generator(uid='bozydar') # Decode token: header_bytes, payload_bytes, signature_bytes = [ base64url_decode(_.encode('ascii')) for _ in token.split(".") ] payload_dict = json.loads(payload_bytes.decode('ascii')) # Rewrite uid and invert token decode procedure. payload_dict['uid'] = 'fafok' payload_bytes = json.dumps(payload_dict).encode('utf-8') forged_token = '.'.join( base64url_encode(_).decode('ascii') for _ in (header_bytes, payload_bytes, signature_bytes)) header = {'Authorization': 'token={}'.format(forged_token)} return header
def _validate(self): if not self._signatures: return payload_str = self._payload for signature in self._signatures: bytes_to_verify = "{0}.{1}".format(signature["protected"], base64url_encode(payload_str)) signer = SIGNER_ALGS[signature["header"]["alg"]] key = keyrep(signature["header"]["jwk"]) gk = key.get_key() sig = base64url_decode(signature["signature"].encode("utf-8")) try: verified = signer.verify(bytes_to_verify, sig, gk) except BadSignature: raise InvalidSchema1Signature() if not verified: raise InvalidSchema1Signature()
def test_authtoken_rs256_anatomy(self): url = Url('/auth/login') credentials = {'uid': self.user1_uid, 'password': self.user1_password} r = requests.post(url, json=credentials) assert r.status_code == 200 token = r.json()['token'] header_bytes, payload_bytes, signature_bytes = [ base64url_decode(_.encode('ascii')) for _ in token.split(".") ] # Make sure the header is valid JSON. header_dict = json.loads(header_bytes.decode('ascii')) assert header_dict['typ'] == "JWT" assert header_dict['alg'] == "RS256" # Make sure that the payload section is valid JSON. payload_dict = json.loads(payload_bytes.decode('ascii')) return token, payload_dict
def _validate(self): if not self._signatures: return payload_str = self._payload for signature in self._signatures: bytes_to_verify = '{0}.{1}'.format(signature['protected'], base64url_encode(payload_str)) signer = SIGNER_ALGS[signature['header']['alg']] key = keyrep(signature['header']['jwk']) gk = key.get_key() sig = base64url_decode(signature['signature'].encode('utf-8')) try: verified = signer.verify(bytes_to_verify, sig, gk) except BadSignature: raise InvalidSchema1Signature() if not verified: raise InvalidSchema1Signature()
def test_custom_json_encoder(self, jws, payload): class CustomJSONEncoder(json.JSONEncoder): def default(self, o): if isinstance(o, Decimal): return "it worked" return super(CustomJSONEncoder, self).default(o) data = {"some_decimal": Decimal("2.2")} with pytest.raises(TypeError): jws.encode(payload, "secret", headers=data) token = jws.encode(payload, "secret", headers=data, json_encoder=CustomJSONEncoder) header = force_bytes(force_unicode(token).split(".")[0]) header = json.loads(force_unicode(base64url_decode(header))) assert "some_decimal" in header assert header["some_decimal"] == "it worked"
def user_data(self, access_token, *args, **kwargs): response = kwargs.get('response') # str() to fix a bug in Python's base64 https://stackoverflow.com/a/2230623/161278 id_token = str(response.get('id_token')) jwt_header_json = base64url_decode(id_token.split('.', 1)[0]) # Decode the JWT header as JSON dict jwt_header = json.loads(jwt_header_json) key = self._get_public_key(jwt_header['kid']) # `kid` is short for key id try: # Retrieve certificate for key_id return jwt_decode( id_token, key=key, algorithms=jwt_header['alg'], # algorithm = 'RS256' audience=self.setting('KEY'), leeway=self.setting('JWT_LEEWAY', default=0), ) except (DecodeError, ExpiredSignature) as error: raise AuthTokenError(self, error)
def decode_value(val): if hasattr(int, 'from_bytes'): int_from_bytes = int.from_bytes else: def int_from_bytes(data, byteorder, signed=False): assert byteorder == 'big' assert not signed if len(data) % 4 != 0: data = (b'\x00' * (4 - (len(data) % 4))) + data result = 0 while len(data) > 0: digit, = struct.unpack('>I', data[:4]) result = (result << 32) + digit data = data[4:] return result if isinstance(val, string_types): val = val.encode('utf-8') decoded = base64url_decode(val) return int_from_bytes(decoded, 'big')
def test_custom_json_encoder(self, jws, payload): class CustomJSONEncoder(json.JSONEncoder): def default(self, o): if isinstance(o, Decimal): return 'it worked' return super(CustomJSONEncoder, self).default(o) data = { 'some_decimal': Decimal('2.2') } with pytest.raises(TypeError): jws.encode(payload, 'secret', headers=data) token = jws.encode(payload, 'secret', headers=data, json_encoder=CustomJSONEncoder) header = ensure_bytes(ensure_unicode(token).split('.')[0]) header = json.loads(ensure_unicode(base64url_decode(header))) assert 'some_decimal' in header assert header['some_decimal'] == 'it worked'
def load_hmac_key(): with open(os.path.join(BASE_PATH, 'jwk_hmac.json'), 'r') as infile: keyobj = json.load(infile) return base64url_decode(ensure_bytes(keyobj['k']))
def decode_value(val): decoded = base64url_decode(ensure_bytes(val)) return int_from_bytes(decoded, 'big')