aboutsummaryrefslogtreecommitdiffstats
path: root/docs/fernet.rst
blob: eacbc2ae3854076ee7ea1b1a2240e8139687c0de (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
Fernet (symmetric encryption)
=============================

.. currentmodule:: cryptography.fernet

Fernet provides guarantees that a message encrypted using it cannot be
manipulated or read without the key. `Fernet`_ is an implementation of
symmetric (also known as "secret key") authenticated cryptography. Fernet also
has support for implementing key rotation via :class:`MultiFernet`.

.. class:: Fernet(key)

    This class provides both encryption and decryption facilities.

    .. doctest::

        >>> from cryptography.fernet import Fernet
        >>> key = Fernet.generate_key()
        >>> f = Fernet(key)
        >>> token = f.encrypt(b"my deep dark secret")
        >>> token
        '...'
        >>> f.decrypt(token)
        'my deep dark secret'

    :param bytes key: A URL-safe base64-encoded 32-byte key. This **must** be
                      kept secret. Anyone with this key is able to create and
                      read messages.

    .. classmethod:: generate_key()

        Generates a fresh fernet key. Keep this some place safe! If you lose it
        you'll no longer be able to decrypt messages; if anyone else gains
        access to it, they'll be able to decrypt all of your messages, and
        they'll also be able forge arbitrary messages that will be
        authenticated and decrypted.

    .. method:: encrypt(data)

        :param bytes data: The message you would like to encrypt.
        :returns bytes: A secure message that cannot be read or altered
                        without the key. It is URL-safe base64-encoded. This is
                        referred to as a "Fernet token".
        :raises TypeError: This exception is raised if ``data`` is not
                           ``bytes``.

        .. note::

            The encrypted message contains the current time when it was
            generated in *plaintext*, the time a message was created will
            therefore be visible to a possible attacker.

    .. method:: decrypt(token, ttl=None)

        :param bytes token: The Fernet token. This is the result of calling
                            :meth:`encrypt`.
        :param int ttl: Optionally, the number of seconds old a message may be
                        for it to be valid. If the message is older than
                        ``ttl`` seconds (from the time it was originally
                        created) an exception will be raised. If ``ttl`` is not
                        provided (or is ``None``), the age of the message is
                        not considered.
        :returns bytes: The original plaintext.
        :raises cryptography.fernet.InvalidToken: If the ``token`` is in any
                                                  way invalid, this exception
                                                  is raised. A token may be
                                                  invalid for a number of
                                                  reasons: it is older than the
                                                  ``ttl``, it is malformed, or
                                                  it does not have a valid
                                                  signature.
        :raises TypeError: This exception is raised if ``token`` is not
                           ``bytes``.


.. class:: MultiFernet(fernets)

    .. versionadded:: 0.7

    This class implements key rotation for Fernet. It takes a ``list`` of
    :class:`Fernet` instances, and implements the same API:

    .. doctest::

        >>> from cryptography.fernet import Fernet, MultiFernet
        >>> key1 = Fernet(Fernet.generate_key())
        >>> key2 = Fernet(Fernet.generate_key())
        >>> f = MultiFernet([key1, key2])
        >>> token = f.encrypt(b"Secret message!")
        >>> token
        '...'
        >>> f.decrypt(token)
        'Secret message!'

    MultiFernet performs all encryption options using the *first* key in the
    ``list`` provided. MultiFernet attempts to decrypt tokens with each key in
    turn. A :class:`cryptography.fernet.InvalidToken` exception is raised if
    the correct key is not found in the ``list`` provided.

    Key rotation makes it easy to replace old keys. You can add your new key at
    the front of the list to start encrypting new messages, and remove old keys
    as they are no longer needed.


.. class:: InvalidToken

    See :meth:`Fernet.decrypt` for more information.

Implementation
--------------

Fernet is built on top of a number of standard cryptographic primitives.
Specifically it uses:

* :class:`~cryptography.hazmat.primitives.ciphers.algorithms.AES` in
  :class:`~cryptography.hazmat.primitives.ciphers.modes.CBC` mode with a
  128-bit key for encryption; using
  :class:`~cryptography.hazmat.primitives.padding.PKCS7` padding.
* :class:`~cryptography.hazmat.primitives.hmac.HMAC` using
  :class:`~cryptography.hazmat.primitives.hashes.SHA256` for authentication.
* Initialization vectors are generated using ``os.urandom()``.

For complete details consult the `specification`_.


.. _`Fernet`: https://github.com/fernet/spec/
.. _`specification`: https://github.com/fernet/spec/blob/master/Spec.md
ass="n">cpu_online_map; EXPORT_SYMBOL(cpu_online_map); cpumask_t cpu_present_map; EXPORT_SYMBOL(cpu_online_map); cpumask_t cpu_possible_map; EXPORT_SYMBOL(cpu_possible_map); struct cpuinfo_arm cpu_data[NR_CPUS]; /* Fake one node for now. See also include/asm-arm/numa.h */ nodemask_t __read_mostly node_online_map = { { [0] = 1UL } }; /* Xen stack for bringing up the first CPU. */ static unsigned char __initdata cpu0_boot_stack[STACK_SIZE] __attribute__((__aligned__(STACK_SIZE))); /* Pointer to the stack, used by head.S when entering C */ unsigned char *init_stack = cpu0_boot_stack; /* Shared state for coordinating CPU bringup */ unsigned long smp_up_cpu = 0; static bool_t cpu_is_dead = 0; /* Number of non-boot CPUs ready to enter C */ unsigned long __initdata ready_cpus = 0; /* ID of the PCPU we're running on */ DEFINE_PER_CPU(unsigned int, cpu_id); /* XXX these seem awfully x86ish... */ /* representing HT siblings of each logical CPU */ DEFINE_PER_CPU_READ_MOSTLY(cpumask_var_t, cpu_sibling_mask); /* representing HT and core siblings of each logical CPU */ DEFINE_PER_CPU_READ_MOSTLY(cpumask_var_t, cpu_core_mask); static void setup_cpu_sibling_map(int cpu) { if ( !zalloc_cpumask_var(&per_cpu(cpu_sibling_mask, cpu)) || !zalloc_cpumask_var(&per_cpu(cpu_core_mask, cpu)) ) panic("No memory for CPU sibling/core maps\n"); /* A CPU is a sibling with itself and is always on its own core. */ cpumask_set_cpu(cpu, per_cpu(cpu_sibling_mask, cpu)); cpumask_set_cpu(cpu, per_cpu(cpu_core_mask, cpu)); } void __init smp_clear_cpu_maps (void) { cpumask_clear(&cpu_possible_map); cpumask_clear(&cpu_online_map); cpumask_set_cpu(0, &cpu_online_map); cpumask_set_cpu(0, &cpu_possible_map); } int __init smp_get_max_cpus (void) { int i, max_cpus = 0; for ( i = 0; i < nr_cpu_ids; i++ ) if ( cpu_possible(i) ) max_cpus++; return max_cpus; } void __init smp_prepare_cpus (unsigned int max_cpus) { cpumask_copy(&cpu_present_map, &cpu_possible_map); setup_cpu_sibling_map(0); } void __init make_cpus_ready(unsigned int max_cpus, unsigned long boot_phys_offset) { unsigned long *gate; paddr_t gate_pa; int i; printk("Waiting for %i other CPUs to be ready\n", max_cpus - 1); /* We use the unrelocated copy of smp_up_cpu as that's the one the * others can see. */ gate_pa = ((paddr_t) (unsigned long) &smp_up_cpu) + boot_phys_offset; gate = map_domain_page(gate_pa >> PAGE_SHIFT) + (gate_pa & ~PAGE_MASK); for ( i = 1; i < max_cpus; i++ ) { /* Tell the next CPU to get ready */ /* TODO: handle boards where CPUIDs are not contiguous */ *gate = i; flush_xen_dcache(*gate); isb(); sev(); /* And wait for it to respond */ while ( ready_cpus < i ) smp_rmb(); } unmap_domain_page(gate); } /* Boot the current CPU */ void __cpuinit start_secondary(unsigned long boot_phys_offset, unsigned long fdt_paddr, unsigned long cpuid) { struct cpuinfo_arm *c = cpu_data + cpuid; memset(get_cpu_info(), 0, sizeof (struct cpu_info)); /* TODO: handle boards where CPUIDs are not contiguous */ set_processor_id(cpuid); *c = boot_cpu_data; identify_cpu(c); init_traps(); setup_virt_paging(); mmu_init_secondary_cpu(); enable_vfp(); gic_init_secondary_cpu(); init_secondary_IRQ(); gic_route_ppis(); init_maintenance_interrupt(); init_timer_interrupt(); set_current(idle_vcpu[cpuid]); setup_cpu_sibling_map(cpuid); /* Run local notifiers */ notify_cpu_starting(cpuid); wmb(); /* Now report this CPU is up */ cpumask_set_cpu(cpuid, &cpu_online_map); wmb(); local_irq_enable(); printk(XENLOG_DEBUG "CPU %u booted.\n", smp_processor_id()); startup_cpu_idle_loop(); } /* Shut down the current CPU */ void __cpu_disable(void) { unsigned int cpu = get_processor_id(); local_irq_disable(); gic_disable_cpu(); /* Allow any queued timer interrupts to get serviced */ local_irq_enable(); mdelay(1); local_irq_disable(); /* It's now safe to remove this processor from the online map */ cpumask_clear_cpu(cpu, &cpu_online_map); if ( cpu_disable_scheduler(cpu) ) BUG(); mb(); /* Return to caller; eventually the IPI mechanism will unwind and the * scheduler will drop to the idle loop, which will call stop_cpu(). */ } void stop_cpu(void) { local_irq_disable(); cpu_is_dead = 1; /* Make sure the write happens before we sleep forever */ dsb(); isb(); while ( 1 ) wfi(); } /* Bring up a remote CPU */ int __cpu_up(unsigned int cpu) { int rc; rc = init_secondary_pagetables(cpu); if ( rc < 0 ) return rc; /* Tell the remote CPU which stack to boot on. */ init_stack = idle_vcpu[cpu]->arch.stack; /* Unblock the CPU. It should be waiting in the loop in head.S * for an event to arrive when smp_up_cpu matches its cpuid. */ smp_up_cpu = cpu; /* we need to make sure that the change to smp_up_cpu is visible to * secondary cpus with D-cache off */ flush_xen_dcache(smp_up_cpu); isb(); sev(); while ( !cpu_online(cpu) ) { cpu_relax(); process_pending_softirqs(); } return 0; } /* Wait for a remote CPU to die */ void __cpu_die(unsigned int cpu) { unsigned int i = 0; while ( !cpu_is_dead ) { mdelay(100); cpu_relax(); process_pending_softirqs(); if ( (++i % 10) == 0 ) printk(KERN_ERR "CPU %u still not dead...\n", cpu); mb(); } cpu_is_dead = 0; mb(); } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * indent-tabs-mode: nil * End: */