# Keys & Signatures

Nekoton-Python provides a comprehensive set of tools for managing cryptographic keys and signatures, which are essential for interacting with the blockchain. This section will guide you through the usage of these tools.

## KeyPair

A `KeyPair`

in cryptographic systems pertains to a duo of a private (or secret) key and a public key. It plays a pivotal role in both encrypting and decrypting data, as well as in digital signatures. Specifically, for Ed25519, a widely-accepted and modern digital signature system, the `KeyPair`

can be generated from a seed or directly from a secret key.

For Ed25519, the `KeyPair`

can also be generated in two primary ways:

- Randomly, utilizing the
`KeyPair.generate()`

method. - Derivatively, from a seed using the
`derive()`

method, which is a feature of the respective seed class.

In the context of Ed25519, the secret key's size is crucial, with the standard mandating a precise length of 32 bytes. This specification ensures the security and consistency of the cryptographic operations facilitated by the key pair.

### Generating a KeyPair

A `KeyPair`

can be generated randomly using the `KeyPair.generate()`

method:

```
keypair = nt.KeyPair.generate()
print(f"Public key: {keypair.public_key}")
print(f"Secret key: {keypair.secret_key}")
```

##### Result

```
Public key: 6b6243e2fa88025d5f2fee051bd8b62ff3da730136c67bbb14033b72c60fb762
Secret key: e7f25e4cf517661a51081779dd7218d42c7db4bb85b18406e337a2ddb4359599
```

## Seeds

A seed is a piece of data that can be used to generate deterministic keys. In Nekoton-Python, two types of seeds are supported: `Bip39`

and `Legacy`

.

### Bip39 Seeds

Bip39 Seeds are 12-word seeds that can be used to derive a `KeyPair`

following the BIP39 standard. They can be generated using the `Bip39Seed.generate()`

method, and a `KeyPair`

can be derived from a Bip39 seed using the `Bip39Seed.derive()`

method.

Example of generating and using a Bip39 seed:

```
bip39_seed = nt.Bip39Seed.generate()
print(bip39_seed)
```

##### Result

```
cricket prize gain hidden dragon fossil repeat blue dream already shaft
exclude
```

Deriving a `KeyPair`

from a Bip39 Seed:

```
keypair = bip39_seed.derive()
print(keypair.public_key, keypair.secret_key)
```

##### Result

```
3247bd75e041a03c9029297b89bb40132744df380596fb4bda4591f1dd9313d7
b"\xbd\xac\xcd\x0e\x89c\xab\xaeZ:!\xf7\xe6\xd9\x9f\xe5a=\x06z0-n'\xc5\xd0\xed\x8e\xee\xb5\x93\x94"
```

### Legacy Seeds

Legacy Seeds are 24-word seeds that can be used to derive a `KeyPair`

. They can be generated using the `LegacySeed.generate()`

method, and a `KeyPair`

can be derived from a legacy seed using the `LegacySeed.derive()`

method.

Example of generating and using a Legacy seed:

```
legacy_seed = nt.LegacySeed.generate()
print(legacy_seed)
```

##### Result

```
away october another abuse bridge woman local lottery ostrich genuine
obvious minor brand wall upper column response bus nose lonely question
useful grocery unable
```

Deriving a `KeyPair`

from a Legacy Seed:

```
keypair = legacy_seed.derive()
print(keypair.public_key, keypair.secret_key)
```

##### Result

```
7ec3e8c544c021808be23b10829440ea45175e76b9d5ede46a7e8d59085c3228
b'{I\xd1\x02*>\x16\x1c\xa7Z\x97\x01<\x1a\x07\x0b\xcc\xb0\x1d\x18i\xbar\xe7aE\x1e\x9d\xb3\xc3\xd8\xaf'
```

### Derivation Path

In the context of BIP39, a derivation path is used to derive different keys from the same seed phrase. It provides a hierarchical structure for generating and organizing keys. With Nekoton-Python, you can retrieve the default derivation path for a specified account number using the `path_for_account`

method.

Here's how you can get the derivation path for a specified account number using a Bip39 seed:

```
path = bip39_seed.path_for_account(0)
print(path) # m/44'/396'/0'/0/1
```

## Public Key Operations

The `nekoton`

library provides various methods to work with public keys. You can initialize, encode, and convert a `PublicKey`

using the provided methods.

### Initialization

Public keys can be initialized from different formats:

#### From Integer

You can initialize a public key from an integer using the `PublicKey.from_int()`

method.

```
public_key = nt.PublicKey.from_int(63837483679490186262641015239053288982995430350508212654141177365814141551489)
print(public_key)
```

##### Result

`8d22bc3f156f400934340607e372076b9a023c6ec5915aa2f790ba9bce088381`

#### From Bytes

You can initialize a public key from bytes using the `PublicKey.from_bytes()`

method.

```
public_key = PublicKey.from_bytes(b'\x8d"\xbc?\x15o@\t44\x06\x07\xe3r\x07k\x9a\x02<n\xc5\x91Z\xa2\xf7\x90\xba\x9b\xce\x08\x83\x81')
print(public_key)
```

##### Result

`8d22bc3f156f400934340607e372076b9a023c6ec5915aa2f790ba9bce088381`

#### From String

You can initialize a public key from a string directly.

```
public_key = PublicKey("8d22bc3f156f400934340607e372076b9a023c6ec5915aa2f790ba9bce088381")
print(public_key)
```

##### Result

`8d22bc3f156f400934340607e372076b9a023c6ec5915aa2f790ba9bce088381`

### Encoding

A `PublicKey`

can be encoded to a string using the `PublicKey.encode()`

method.

```
encoded = public_key.encode()
print(encoded)
```

##### Result

`8d22bc3f156f400934340607e372076b9a023c6ec5915aa2f790ba9bce088381`

### Byte Conversion

Convert a `PublicKey`

to bytes using the `PublicKey.to_bytes()`

method.

```
bytes_representation = public_key.to_bytes()
print(bytes_representation)
```

##### Result

`b'\x8d"\xbc?\x15o@\t44\x06\x07\xe3r\x07k\x9a\x02<n\xc5\x91Z\xa2\xf7\x90\xba\x9b\xce\x08\x83\x81'`

This structure provides a clear distinction between the different methods of initializing a `PublicKey`

, as well as its encoding and conversion functionalities.

## Signing Data

Signing data is a fundamental cryptographic operation that provides both authentication and data integrity. By creating a digital signature for a specific set of data, you not only prove the origin of that data (authentication) but also confirm that the data hasn't been tampered with since the signature was created (integrity). This process can be accomplished using different methods, depending on the requirements of the specific application or system.

In this section, we'll explore two primary ways of signing data: with hashing (using the `sign()`

method) and without hashing (using the `sign_raw()`

method).

Additionally, we will touch upon the optional incorporation of a `signature_id`

to further enhance the identification of the signed data.

### Data with Hashing

When signing data using the `sign()`

method, the data is first hashed before being signed. This is useful when you want to ensure the integrity of the data being signed.

```
data = b"Hello, World 42!"
signature_id = await transport.get_signature_id() # Optional
signature = keypair.sign(data, signature_id)
print(signature)
```

##### Result

`Signature('b2bd3045b3ec3872bcccc96f58b71fe0fd60cba104249cc5e72c1a2ebad35cbbf1d82a631dda5cc7a8f07b540fb1564edfa0920ede751a59e08d3ed54f80e908')`

TIP

The `signature_id`

is an optional identifier for a signature, sourced directly from the transport layer using the `get_signature_id()`

method. If this is your first time encountering `signature_id`

or you're unfamiliar with our transport layer, it's recommended to read our guide on working with the transport to get started.

### Raw Data

The `sign_raw()`

method signs the data directly without hashing it. This can be useful if you need to sign data that doesn't require hashing or in cases where the data has already been hashed:

```
data = b"Hello, World 42!"
signature_raw = keypair.sign_raw(data)
print(signature_raw)
```

##### Result

`Signature('ce998c9cf3dcf0aecd2b3a372661e7d75946fd3f2dfa793a4f3944da985fe49ad9c6e23b2fbce2dee31b802a3bff646ff5ac268a4a54c7aa319882e4817b4504')`

## Verifying Signatures

To verify a signature, you need to use the correct input depending on the signing method. If the data was signed with `sign()`

, you should use the hashed data.

If it was signed with `sign_raw()`

, you should either use the hash of the original data (if you want to verify the signature against hashed data) or the original data itself (if you want to verify the signature against raw data).

When a `signature_id`

was used during signing, the same `signature_id`

should be used for verification.

### Hashed Signature

To verify a hashed signature, you will first need to hash the original data using the SHA-256 algorithm, and then call the `check_signature()`

method:

```
import hashlib
data = hashlib.sha256(b"Hello, World 42!").digest()
is_valid = public_key.check_signature(data, signature, signature_id)
print(is_valid) # True
```

### Raw Signature

To verify a raw signature, you will need to call the `check_signature_raw()`

method with the original raw data and the signature:

```
data = hashlib.sha256(b"Hello, World 42!").digest()
signature_raw = keypair.sign_raw(data)
is_valid_raw = public_key.check_signature_raw(data, signature_raw)
print(is_valid_raw) # True
```