def verify(self, sign: bytes, data: bytes) -> None: with tempfile.TemporaryDirectory() as tmpdir: public_keyset_filename = os.path.join(tmpdir, 'public_keyset_file') with open(public_keyset_filename, 'wb') as f: cleartext_keyset_handle.write(tink.BinaryKeysetWriter(f), self._public_keyset_handle) signature_filename = os.path.join(tmpdir, 'signature_file') with open(signature_filename, 'wb') as f: f.write(sign) message_filename = os.path.join(tmpdir, 'message_file') with open(message_filename, 'wb') as f: f.write(data) output_filename = os.path.join(tmpdir, 'output_file') try: unused_return_value = subprocess.check_output([ self._cli, public_keyset_filename, signature_filename, message_filename, output_filename ]) except subprocess.CalledProcessError as e: raise tink.TinkError(e) with open(output_filename, 'rb') as f: output = f.read() if output != b'valid': raise tink.TinkError('verification failed') return None
def verify_mac(self, mac_value: bytes, data: bytes) -> None: with tempfile.TemporaryDirectory() as tmpdir: keyset_filename = os.path.join(tmpdir, 'keyset_file') with open(keyset_filename, 'wb') as f: cleartext_keyset_handle.write(tink.BinaryKeysetWriter(f), self._keyset_handle) data_filename = os.path.join(tmpdir, 'data_file') with open(data_filename, 'wb') as f: f.write(data) mac_filename = os.path.join(tmpdir, 'mac_file') with open(mac_filename, 'wb') as f: f.write(mac_value) result_filename = os.path.join(tmpdir, 'result_file') try: unused_return_value = subprocess.check_output([ self._cli, keyset_filename, 'verify', data_filename, mac_filename, result_filename ]) except subprocess.CalledProcessError as e: raise tink.TinkError(e) with open(result_filename, 'rb') as f: result = f.read() if result != b'valid': raise tink.TinkError('verification failed') return None
def _convert_to_ecdsa_key( key: Dict[str, Union[str, List[str]]]) -> tink_pb2.Keyset.Key: """Converts a EC Json Web Key (JWK) into a tink_pb2.Keyset.Key.""" ecdsa_public_key = jwt_ecdsa_pb2.JwtEcdsaPublicKey() algorithm = _ECDSA_NAME_TO_ALGORITHM.get(key['alg'], None) if not algorithm: raise tink.TinkError('unknown ECDSA algorithm') if key.get('kty', None) != 'EC': raise tink.TinkError('invalid kty') _, crv = _ECDSA_PARAMS[algorithm] if key.get('crv', None) != crv: raise tink.TinkError('invalid crv') _validate_use_and_key_ops(key) if 'd' in key: raise tink.TinkError('cannot convert private ECDSA key') ecdsa_public_key.algorithm = algorithm ecdsa_public_key.x = _base64_decode(key['x']) ecdsa_public_key.y = _base64_decode(key['y']) if 'kid' in key: ecdsa_public_key.custom_kid.value = key['kid'] proto_key = tink_pb2.Keyset.Key() proto_key.key_data.type_url = _JWT_ECDSA_PUBLIC_KEY_TYPE proto_key.key_data.value = ecdsa_public_key.SerializeToString() proto_key.key_data.key_material_type = tink_pb2.KeyData.ASYMMETRIC_PUBLIC proto_key.output_prefix_type = tink_pb2.RAW proto_key.status = tink_pb2.ENABLED return proto_key
def _convert_to_rsa_ssa_pss_key( key: Dict[str, Union[str, List[str]]]) -> tink_pb2.Keyset.Key: """Converts a JWK into a JwtEcdsaPublicKey.""" public_key = jwt_rsa_ssa_pss_pb2.JwtRsaSsaPssPublicKey() algorithm = _RSA_SSA_PSS_NAME_TO_ALGORITHM.get(key['alg'], None) if not algorithm: raise tink.TinkError('unknown RSA SSA PSS algorithm') if key.get('kty', None) != 'RSA': raise tink.TinkError('invalid kty') _validate_use_and_key_ops(key) if ('p' in key or 'q' in key or 'dp' in key or 'dq' in key or 'd' in key or 'qi' in key): raise tink.TinkError('importing RSA private keys is not implemented') public_key.algorithm = algorithm public_key.n = _base64_decode(key['n']) public_key.e = _base64_decode(key['e']) if 'kid' in key: public_key.custom_kid.value = key['kid'] proto_key = tink_pb2.Keyset.Key() proto_key.key_data.type_url = _JWT_RSA_SSA_PSS_PUBLIC_KEY_TYPE proto_key.key_data.value = public_key.SerializeToString() proto_key.key_data.key_material_type = tink_pb2.KeyData.ASYMMETRIC_PUBLIC proto_key.output_prefix_type = tink_pb2.RAW proto_key.status = tink_pb2.ENABLED return proto_key
def _validate_use_and_key_ops(key: Dict[str, Union[str, List[str]]]): """Checks that 'key_ops' and 'use' have the right values if present.""" if 'key_ops' in key: key_ops = key['key_ops'] if len(key_ops) != 1 or key_ops[0] != 'verify': raise tink.TinkError('invalid key_ops') if 'use' in key and key['use'] != 'sig': raise tink.TinkError('invalid use')
def disable_key(self, key_id: int) -> None: """Disables a key.""" for key in self._keyset.key: if key.key_id == key_id: key.status = tink_pb2.DISABLED return raise tink.TinkError('key not found: %d' % key_id)
def public_keyset(stub: testing_api_pb2_grpc.KeysetStub, private_keyset: bytes) -> bytes: request = testing_api_pb2.KeysetPublicRequest(private_keyset=private_keyset) response = stub.Public(request) if response.err: raise tink.TinkError(response.err) return response.public_keyset
def sign_and_encode(self, raw_jwt: jwt.RawJwt) -> Text: request = testing_api_pb2.JwtSignRequest( keyset=self._keyset, raw_jwt=raw_jwt_to_proto(raw_jwt)) response = self._stub.PublicKeySignAndEncode(request) if response.err: raise tink.TinkError(response.err) return response.signed_compact_jwt
def _run(self, operation: Text, input_data: bytes, associated_data: bytes) -> bytes: with tempfile.TemporaryDirectory() as tmpdir: keyset_filename = os.path.join(tmpdir, 'keyset_file') input_filename = os.path.join(tmpdir, 'input_file') associated_data_filename = os.path.join(tmpdir, 'associated_data_file') output_filename = os.path.join(tmpdir, 'output_file') with open(keyset_filename, 'wb') as f: cleartext_keyset_handle.write(tink.BinaryKeysetWriter(f), self._keyset_handle) with open(input_filename, 'wb') as f: f.write(input_data) with open(associated_data_filename, 'wb') as f: f.write(associated_data) try: unused_return_value = subprocess.check_output([ self._cli, keyset_filename, operation, input_filename, associated_data_filename, output_filename ]) except subprocess.CalledProcessError as e: raise tink.TinkError(e) with open(output_filename, 'rb') as f: output_data = f.read() return output_data
def delete_key(self, key_id: int) -> None: """Deletes a key.""" for key in self._keyset.key: if key.key_id == key_id: self._keyset.key.remove(key) return raise tink.TinkError('key not found: %d' % key_id)
def sign(self, data: bytes) -> bytes: request = testing_api_pb2.SignatureSignRequest( private_keyset=self._private_handle, data=data) response = self._stub.Sign(request) if response.err: raise tink.TinkError(response.err) return response.signature
def set_primary_key(self, key_id: int) -> None: """Sets a key as primary.""" for key in self._keyset.key: if key.key_id == key_id: self._keyset.primary_key_id = key_id return raise tink.TinkError('key not found: %d' % key_id)
def compute_mac_and_encode(self, raw_jwt: jwt.RawJwt) -> str: request = testing_api_pb2.JwtSignRequest( keyset=self._keyset, raw_jwt=raw_jwt_to_proto(raw_jwt)) response = self._stub.ComputeMacAndEncode(request) if response.err: raise tink.TinkError(response.err) return response.signed_compact_jwt
def jwk_set_from_keyset(stub: testing_api_pb2_grpc.JwtStub, keyset: bytes) -> str: request = testing_api_pb2.JwtToJwkSetRequest(keyset=keyset) response = stub.ToJwkSet(request) if response.err: raise tink.TinkError(response.err) return response.jwk_set
def jwk_set_to_keyset(stub: testing_api_pb2_grpc.JwtStub, jwk_set: str) -> bytes: request = testing_api_pb2.JwtFromJwkSetRequest(jwk_set=jwk_set) response = stub.FromJwkSet(request) if response.err: raise tink.TinkError(response.err) return response.keyset
def keyset_to_json(stub: testing_api_pb2_grpc.KeysetStub, keyset: bytes) -> Text: request = testing_api_pb2.KeysetToJsonRequest(keyset=keyset) response = stub.ToJson(request) if response.err: raise tink.TinkError(response.err) return response.json_keyset
def keyset_from_json(stub: testing_api_pb2_grpc.KeysetStub, json_keyset: Text) -> bytes: request = testing_api_pb2.KeysetFromJsonRequest(json_keyset=json_keyset) response = stub.FromJson(request) if response.err: raise tink.TinkError(response.err) return response.keyset
def _set_custom_kid(keyset_handle: tink.KeysetHandle, custom_kid: str) -> tink.KeysetHandle: """Sets the custom_kid field of the first key.""" buffer = io.BytesIO() cleartext_keyset_handle.write( tink.BinaryKeysetWriter(buffer), keyset_handle) keyset = tink_pb2.Keyset.FromString(buffer.getvalue()) if keyset.key[0].key_data.type_url.endswith('JwtEcdsaPrivateKey'): jwt_ecdsa_key = jwt_ecdsa_pb2.JwtEcdsaPrivateKey.FromString( keyset.key[0].key_data.value) jwt_ecdsa_key.public_key.custom_kid.value = custom_kid keyset.key[0].key_data.value = jwt_ecdsa_key.SerializeToString() elif keyset.key[0].key_data.type_url.endswith('JwtRsaSsaPkcs1PrivateKey'): rsa_key = jwt_rsa_ssa_pkcs1_pb2.JwtRsaSsaPkcs1PrivateKey.FromString( keyset.key[0].key_data.value) rsa_key.public_key.custom_kid.value = custom_kid keyset.key[0].key_data.value = rsa_key.SerializeToString() elif keyset.key[0].key_data.type_url.endswith('JwtRsaSsaPssPrivateKey'): rsa_key = jwt_rsa_ssa_pss_pb2.JwtRsaSsaPssPrivateKey.FromString( keyset.key[0].key_data.value) rsa_key.public_key.custom_kid.value = custom_kid keyset.key[0].key_data.value = rsa_key.SerializeToString() else: raise tink.TinkError('unknown key type') return cleartext_keyset_handle.from_keyset(keyset)
def verify_mac(self, mac_value: bytes, data: bytes) -> None: request = testing_api_pb2.VerifyMacRequest(keyset=self._keyset, mac_value=mac_value, data=data) response = self._stub.VerifyMac(request) if response.err: raise tink.TinkError(response.err)
def compute_mac(self, data: bytes) -> bytes: request = testing_api_pb2.ComputeMacRequest(keyset=self._keyset, data=data) response = self._stub.ComputeMac(request) if response.err: raise tink.TinkError(response.err) return response.mac_value
def key_template(stub: testing_api_pb2_grpc.KeysetStub, template_name: str) -> tink_pb2.KeyTemplate: request = testing_api_pb2.KeysetTemplateRequest(template_name=template_name) response = stub.GetTemplate(request) if response.err: raise tink.TinkError(response.err) return tink_pb2.KeyTemplate.FromString(response.key_template)
def from_public_keyset_handle(keyset_handle: tink.KeysetHandle) -> str: """Converts a Tink KeysetHandle with JWT keys into a Json Web Key (JWK) set. JWK is defined in https://www.rfc-editor.org/rfc/rfc7517.txt. Disabled keys are skipped. Keys with output prefix type "TINK" will include the encoded key ID as "kid" value. Keys with output prefix type "RAW" will not have a "kid" value set. Currently, public keys for algorithms ES256, ES384, ES512, RS256, RS384, RS512, PS256, PS384 and PS512 supported. Args: keyset_handle: A Tink KeysetHandle that contains JWT Keys. Returns: A JWK set, which is a JSON encoded string. Raises: TinkError if the keys are not of the expected type, or if they have a ouput prefix type that is not supported. """ output_stream = io.BytesIO() writer = tink.BinaryKeysetWriter(output_stream) keyset_handle.write_no_secret(writer) keyset = tink_pb2.Keyset.FromString(output_stream.getvalue()) keys = [] for key in keyset.key: if key.status != tink_pb2.ENABLED: continue if key.key_data.key_material_type != tink_pb2.KeyData.ASYMMETRIC_PUBLIC: raise tink.TinkError('wrong key material type') if key.output_prefix_type not in [tink_pb2.RAW, tink_pb2.TINK]: raise tink.TinkError('unsupported output prefix type') if key.key_data.type_url == _JWT_ECDSA_PUBLIC_KEY_TYPE: keys.append(_convert_jwt_ecdsa_key(key)) elif key.key_data.type_url == _JWT_RSA_SSA_PKCS1_PUBLIC_KEY_TYPE: keys.append(_convert_jwt_rsa_ssa_pkcs1_key(key)) elif key.key_data.type_url == _JWT_RSA_SSA_PSS_PUBLIC_KEY_TYPE: keys.append(_convert_jwt_rsa_ssa_pss_key(key)) else: raise tink.TinkError('unknown key type: %s' % key.key_data.type_url) return json.dumps({'keys': keys}, separators=(',', ':'))
def to_public_keyset_handle(jwk_set: str) -> tink.KeysetHandle: """Converts a Json Web Key (JWK) set into a Tink KeysetHandle with JWT keys. JWK is defined in https://www.rfc-editor.org/rfc/rfc7517.txt. All keys are converted into Tink keys with output prefix type "RAW". Currently, public keys for algorithms ES256, ES384, ES512, RS256, RS384, RS512, PS256, PS384 and PS512 supported. Args: jwk_set: A JWK set, which is a JSON encoded string. Returns: A tink.KeysetHandle. Raises: TinkError if the key cannot be converted. """ try: keys_dict = json.loads(jwk_set) except json.decoder.JSONDecodeError as e: raise tink.TinkError('error parsing JWK set: %s' % e.msg) if 'keys' not in keys_dict: raise tink.TinkError('invalid JWK set: keys not found') proto_keyset = tink_pb2.Keyset() for key in keys_dict['keys']: if 'alg' not in key: raise tink.TinkError('invalid JWK: alg not found') alg = key['alg'] if alg.startswith('ES'): proto_key = _convert_to_ecdsa_key(key) elif alg.startswith('RS'): proto_key = _convert_to_rsa_ssa_pkcs1_key(key) elif alg.startswith('PS'): proto_key = _convert_to_rsa_ssa_pss_key(key) else: raise tink.TinkError('unknown alg') new_id = _generate_unused_key_id(proto_keyset) proto_key.key_id = new_id proto_keyset.key.append(proto_key) # JWK sets do not really have a primary key (see RFC 7517, Section 5.1). # To verify signature, it also does not matter which key is primary. We # simply set it to the last key. proto_keyset.primary_key_id = new_id return cleartext_keyset_handle.from_keyset(proto_keyset)
def new_keyset(stub: testing_api_pb2_grpc.KeysetStub, key_template: tink_pb2.KeyTemplate) -> bytes: gen_request = testing_api_pb2.KeysetGenerateRequest( template=key_template.SerializeToString()) gen_response = stub.Generate(gen_request) if gen_response.err: raise tink.TinkError(gen_response.err) return gen_response.keyset
def decrypt(self, ciphertext: bytes, associated_data: bytes) -> bytes: dec_request = testing_api_pb2.AeadDecryptRequest( keyset=self._keyset, ciphertext=ciphertext, associated_data=associated_data) dec_response = self._stub.Decrypt(dec_request) if dec_response.err: raise tink.TinkError(dec_response.err) return dec_response.plaintext
def encrypt(self, plaintext: bytes, associated_data: bytes) -> bytes: enc_request = testing_api_pb2.AeadEncryptRequest( keyset=self._keyset, plaintext=plaintext, associated_data=associated_data) enc_response = self._stub.Encrypt(enc_request) if enc_response.err: raise tink.TinkError(enc_response.err) return enc_response.ciphertext
def verify_mac(self, mac_value: bytes, data: bytes) -> None: logging.info('verify_mac in lang %s.', self.lang) request = testing_api_pb2.VerifyMacRequest(keyset=self._keyset, mac_value=mac_value, data=data) response = self._stub.VerifyMac(request) if response.err: logging.info('error verify_mac in %s: %s', self.lang, response.err) raise tink.TinkError(response.err)
def verify(self, signature: bytes, data: bytes) -> None: logging.info('signature verify in lang %s.', self.lang) request = testing_api_pb2.SignatureVerifyRequest( public_keyset=self._public_handle, signature=signature, data=data) response = self._stub.Verify(request) if response.err: logging.info('error signature verify in %s: %s', self.lang, response.err) raise tink.TinkError(response.err)
def encrypt(self, plaintext: bytes, context_info: bytes) -> bytes: enc_request = testing_api_pb2.HybridEncryptRequest( public_keyset=self._public_handle, plaintext=plaintext, context_info=context_info) enc_response = self._stub.Encrypt(enc_request) if enc_response.err: raise tink.TinkError(enc_response.err) return enc_response.ciphertext
def decrypt(self, ciphertext: bytes, context_info: bytes) -> bytes: dec_request = testing_api_pb2.HybridDecryptRequest( private_keyset=self._private_handle, ciphertext=ciphertext, context_info=context_info) dec_response = self._stub.Decrypt(dec_request) if dec_response.err: raise tink.TinkError(dec_response.err) return dec_response.plaintext