/usr/lib/python2.7/dist-packages/pskc/aeskw.py is in python-pskc 0.2-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
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 | # aeskw.py - implementation of AES key wrapping
# coding: utf-8
#
# Copyright (C) 2014 Arthur de Jong
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301 USA
"""Implement key wrapping as described in RFC 3394 and RFC 5649."""
from Crypto.Cipher import AES
from Crypto.Util.number import bytes_to_long, long_to_bytes
from Crypto.Util.strxor import strxor
from pskc.exceptions import EncryptionError, DecryptionError
def _split(value):
return value[:8], value[8:]
RFC3394_IV = 'a6a6a6a6a6a6a6a6'.decode('hex')
RFC5649_IV = 'a65959a6'.decode('hex')
def wrap(plaintext, key, iv=None, pad=None):
"""Apply the AES key wrap algorithm to the plaintext.
The iv can specify an initial value, otherwise the value from RFC 3394 or
RFC 5649 will be used, depending on the plaintext length and the value of
pad.
If pad is True, padding as described in RFC 5649 will always be used. If
pad is False, padding is disabled. Other values automatically enable RFC
5649 padding when needed."""
if iv is not None:
pad = False
mli = len(plaintext)
if pad is False and (mli % 8 != 0 or mli < 16):
raise EncryptionError('Plaintext length wrong')
if mli % 8 != 0 and pad is not False:
r = (mli + 7) // 8
plaintext += ((r * 8) - mli) * '\0'
if iv is None:
if len(plaintext) != mli or pad is True:
iv = RFC5649_IV + long_to_bytes(mli, 4)
else:
iv = RFC3394_IV
encrypt = AES.new(key).encrypt
n = len(plaintext) / 8
if n == 1:
# RFC 5649 shortcut
return encrypt(iv + plaintext)
A = iv
R = [plaintext[i * 8:i * 8 + 8]
for i in range(n)]
for j in range(6):
for i in range(n):
A, R[i] = _split(encrypt(A + R[i]))
A = strxor(A, long_to_bytes(n * j + i + 1, 8))
return A + ''.join(R)
def unwrap(ciphertext, key, iv=None, pad=None):
"""Apply the AES key unwrap algorithm to the ciphertext.
The iv can specify an initial value, otherwise the value from RFC 3394 or
RFC 5649 will be used, depending on the value of pad.
If pad is False, unpadding as described in RFC 5649 will be disabled,
otherwise checking and removing the padding is automatically done."""
if iv is not None:
pad = False
if len(ciphertext) % 8 != 0 or (pad is False and len(ciphertext) < 24):
raise DecryptionError('Ciphertext length wrong')
decrypt = AES.new(key).decrypt
n = len(ciphertext) / 8 - 1
if n == 1:
A, plaintext = _split(decrypt(ciphertext))
else:
A = ciphertext[:8]
R = [ciphertext[(i + 1) * 8:(i + 2) * 8]
for i in range(n)]
for j in reversed(range(6)):
for i in reversed(range(n)):
A = strxor(A, long_to_bytes(n * j + i + 1, 8))
A, R[i] = _split(decrypt(A + R[i]))
plaintext = ''.join(R)
if iv is None:
if A == RFC3394_IV and pad is not True:
return plaintext
elif A[:4] == RFC5649_IV and pad is not False:
mli = bytes_to_long(A[4:])
# check padding length is valid and only contains zeros
if 8 * (n - 1) < mli <= 8 * n and \
all(x == '\0' for x in plaintext[mli:]):
return plaintext[:mli]
elif A == iv:
return plaintext
raise DecryptionError('IV does not match')
|