summaryrefslogtreecommitdiff
path: root/wheel/signatures
diff options
context:
space:
mode:
authorJason R. Coombs <jaraco@jaraco.com>2017-11-12 13:40:27 -0500
committerJason R. Coombs <jaraco@jaraco.com>2017-11-12 13:40:27 -0500
commitff497abc7d922edf8a0221946dd41445be7f968c (patch)
tree8b831f0f305268c9429fe4bcd1afe13452a0934b /wheel/signatures
parent5eb85d0e1f9064a645c490cfdde9bb246af986b2 (diff)
downloadwheel-ff497abc7d922edf8a0221946dd41445be7f968c.tar.gz
Removing everything
Diffstat (limited to 'wheel/signatures')
-rw-r--r--wheel/signatures/__init__.py106
-rw-r--r--wheel/signatures/djbec.py270
-rw-r--r--wheel/signatures/ed25519py.py52
-rw-r--r--wheel/signatures/keys.py99
4 files changed, 0 insertions, 527 deletions
diff --git a/wheel/signatures/__init__.py b/wheel/signatures/__init__.py
deleted file mode 100644
index 3f21b50..0000000
--- a/wheel/signatures/__init__.py
+++ /dev/null
@@ -1,106 +0,0 @@
-"""
-Create and verify jws-js format Ed25519 signatures.
-"""
-
-__all__ = [ 'sign', 'verify' ]
-
-import json
-from ..util import urlsafe_b64decode, urlsafe_b64encode, native, binary
-
-ed25519ll = None
-
-ALG = "Ed25519"
-
-def get_ed25519ll():
- """Lazy import-and-test of ed25519 module"""
- global ed25519ll
-
- if not ed25519ll:
- try:
- import ed25519ll # fast (thousands / s)
- except (ImportError, OSError): # pragma nocover
- from . import ed25519py as ed25519ll # pure Python (hundreds / s)
- test()
-
- return ed25519ll
-
-def sign(payload, keypair):
- """Return a JWS-JS format signature given a JSON-serializable payload and
- an Ed25519 keypair."""
- get_ed25519ll()
- #
- header = {
- "alg": ALG,
- "jwk": {
- "kty": ALG, # alg -> kty in jwk-08.
- "vk": native(urlsafe_b64encode(keypair.vk))
- }
- }
-
- encoded_header = urlsafe_b64encode(binary(json.dumps(header, sort_keys=True)))
- encoded_payload = urlsafe_b64encode(binary(json.dumps(payload, sort_keys=True)))
- secured_input = b".".join((encoded_header, encoded_payload))
- sig_msg = ed25519ll.crypto_sign(secured_input, keypair.sk)
- signature = sig_msg[:ed25519ll.SIGNATUREBYTES]
- encoded_signature = urlsafe_b64encode(signature)
-
- return {"recipients":
- [{"header":native(encoded_header),
- "signature":native(encoded_signature)}],
- "payload": native(encoded_payload)}
-
-def assertTrue(condition, message=""):
- if not condition:
- raise ValueError(message)
-
-def verify(jwsjs):
- """Return (decoded headers, payload) if all signatures in jwsjs are
- consistent, else raise ValueError.
-
- Caller must decide whether the keys are actually trusted."""
- get_ed25519ll()
- # XXX forbid duplicate keys in JSON input using object_pairs_hook (2.7+)
- recipients = jwsjs["recipients"]
- encoded_payload = binary(jwsjs["payload"])
- headers = []
- for recipient in recipients:
- assertTrue(len(recipient) == 2, "Unknown recipient key {0}".format(recipient))
- h = binary(recipient["header"])
- s = binary(recipient["signature"])
- header = json.loads(native(urlsafe_b64decode(h)))
- assertTrue(header["alg"] == ALG,
- "Unexpected algorithm {0}".format(header["alg"]))
- if "alg" in header["jwk"] and not "kty" in header["jwk"]:
- header["jwk"]["kty"] = header["jwk"]["alg"] # b/w for JWK < -08
- assertTrue(header["jwk"]["kty"] == ALG, # true for Ed25519
- "Unexpected key type {0}".format(header["jwk"]["kty"]))
- vk = urlsafe_b64decode(binary(header["jwk"]["vk"]))
- secured_input = b".".join((h, encoded_payload))
- sig = urlsafe_b64decode(s)
- sig_msg = sig+secured_input
- verified_input = native(ed25519ll.crypto_sign_open(sig_msg, vk))
- verified_header, verified_payload = verified_input.split('.')
- verified_header = binary(verified_header)
- decoded_header = native(urlsafe_b64decode(verified_header))
- headers.append(json.loads(decoded_header))
-
- verified_payload = binary(verified_payload)
-
- # only return header, payload that have passed through the crypto library.
- payload = json.loads(native(urlsafe_b64decode(verified_payload)))
-
- return headers, payload
-
-def test():
- kp = ed25519ll.crypto_sign_keypair()
- payload = {'test': 'onstartup'}
- jwsjs = json.loads(json.dumps(sign(payload, kp)))
- verify(jwsjs)
- jwsjs['payload'] += 'x'
- try:
- verify(jwsjs)
- except ValueError:
- pass
- else: # pragma no cover
- raise RuntimeError("No error from bad wheel.signatures payload.")
-
diff --git a/wheel/signatures/djbec.py b/wheel/signatures/djbec.py
deleted file mode 100644
index 56efe44..0000000
--- a/wheel/signatures/djbec.py
+++ /dev/null
@@ -1,270 +0,0 @@
-# Ed25519 digital signatures
-# Based on http://ed25519.cr.yp.to/python/ed25519.py
-# See also http://ed25519.cr.yp.to/software.html
-# Adapted by Ron Garret
-# Sped up considerably using coordinate transforms found on:
-# http://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html
-# Specifically add-2008-hwcd-4 and dbl-2008-hwcd
-
-try: # pragma nocover
- unicode
- PY3 = False
- def asbytes(b):
- """Convert array of integers to byte string"""
- return ''.join(chr(x) for x in b)
- def joinbytes(b):
- """Convert array of bytes to byte string"""
- return ''.join(b)
- def bit(h, i):
- """Return i'th bit of bytestring h"""
- return (ord(h[i//8]) >> (i%8)) & 1
-
-except NameError: # pragma nocover
- PY3 = True
- asbytes = bytes
- joinbytes = bytes
- def bit(h, i):
- return (h[i//8] >> (i%8)) & 1
-
-import hashlib
-
-b = 256
-q = 2**255 - 19
-l = 2**252 + 27742317777372353535851937790883648493
-
-def H(m):
- return hashlib.sha512(m).digest()
-
-def expmod(b, e, m):
- if e == 0: return 1
- t = expmod(b, e // 2, m) ** 2 % m
- if e & 1: t = (t * b) % m
- return t
-
-# Can probably get some extra speedup here by replacing this with
-# an extended-euclidean, but performance seems OK without that
-def inv(x):
- return expmod(x, q-2, q)
-
-d = -121665 * inv(121666)
-I = expmod(2,(q-1)//4,q)
-
-def xrecover(y):
- xx = (y*y-1) * inv(d*y*y+1)
- x = expmod(xx,(q+3)//8,q)
- if (x*x - xx) % q != 0: x = (x*I) % q
- if x % 2 != 0: x = q-x
- return x
-
-By = 4 * inv(5)
-Bx = xrecover(By)
-B = [Bx % q,By % q]
-
-#def edwards(P,Q):
-# x1 = P[0]
-# y1 = P[1]
-# x2 = Q[0]
-# y2 = Q[1]
-# x3 = (x1*y2+x2*y1) * inv(1+d*x1*x2*y1*y2)
-# y3 = (y1*y2+x1*x2) * inv(1-d*x1*x2*y1*y2)
-# return (x3 % q,y3 % q)
-
-#def scalarmult(P,e):
-# if e == 0: return [0,1]
-# Q = scalarmult(P,e/2)
-# Q = edwards(Q,Q)
-# if e & 1: Q = edwards(Q,P)
-# return Q
-
-# Faster (!) version based on:
-# http://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html
-
-def xpt_add(pt1, pt2):
- (X1, Y1, Z1, T1) = pt1
- (X2, Y2, Z2, T2) = pt2
- A = ((Y1-X1)*(Y2+X2)) % q
- B = ((Y1+X1)*(Y2-X2)) % q
- C = (Z1*2*T2) % q
- D = (T1*2*Z2) % q
- E = (D+C) % q
- F = (B-A) % q
- G = (B+A) % q
- H = (D-C) % q
- X3 = (E*F) % q
- Y3 = (G*H) % q
- Z3 = (F*G) % q
- T3 = (E*H) % q
- return (X3, Y3, Z3, T3)
-
-def xpt_double (pt):
- (X1, Y1, Z1, _) = pt
- A = (X1*X1)
- B = (Y1*Y1)
- C = (2*Z1*Z1)
- D = (-A) % q
- J = (X1+Y1) % q
- E = (J*J-A-B) % q
- G = (D+B) % q
- F = (G-C) % q
- H = (D-B) % q
- X3 = (E*F) % q
- Y3 = (G*H) % q
- Z3 = (F*G) % q
- T3 = (E*H) % q
- return (X3, Y3, Z3, T3)
-
-def pt_xform (pt):
- (x, y) = pt
- return (x, y, 1, (x*y)%q)
-
-def pt_unxform (pt):
- (x, y, z, _) = pt
- return ((x*inv(z))%q, (y*inv(z))%q)
-
-def xpt_mult (pt, n):
- if n==0: return pt_xform((0,1))
- _ = xpt_double(xpt_mult(pt, n>>1))
- return xpt_add(_, pt) if n&1 else _
-
-def scalarmult(pt, e):
- return pt_unxform(xpt_mult(pt_xform(pt), e))
-
-def encodeint(y):
- bits = [(y >> i) & 1 for i in range(b)]
- e = [(sum([bits[i * 8 + j] << j for j in range(8)]))
- for i in range(b//8)]
- return asbytes(e)
-
-def encodepoint(P):
- x = P[0]
- y = P[1]
- bits = [(y >> i) & 1 for i in range(b - 1)] + [x & 1]
- e = [(sum([bits[i * 8 + j] << j for j in range(8)]))
- for i in range(b//8)]
- return asbytes(e)
-
-def publickey(sk):
- h = H(sk)
- a = 2**(b-2) + sum(2**i * bit(h,i) for i in range(3,b-2))
- A = scalarmult(B,a)
- return encodepoint(A)
-
-def Hint(m):
- h = H(m)
- return sum(2**i * bit(h,i) for i in range(2*b))
-
-def signature(m,sk,pk):
- h = H(sk)
- a = 2**(b-2) + sum(2**i * bit(h,i) for i in range(3,b-2))
- inter = joinbytes([h[i] for i in range(b//8,b//4)])
- r = Hint(inter + m)
- R = scalarmult(B,r)
- S = (r + Hint(encodepoint(R) + pk + m) * a) % l
- return encodepoint(R) + encodeint(S)
-
-def isoncurve(P):
- x = P[0]
- y = P[1]
- return (-x*x + y*y - 1 - d*x*x*y*y) % q == 0
-
-def decodeint(s):
- return sum(2**i * bit(s,i) for i in range(0,b))
-
-def decodepoint(s):
- y = sum(2**i * bit(s,i) for i in range(0,b-1))
- x = xrecover(y)
- if x & 1 != bit(s,b-1): x = q-x
- P = [x,y]
- if not isoncurve(P): raise Exception("decoding point that is not on curve")
- return P
-
-def checkvalid(s, m, pk):
- if len(s) != b//4: raise Exception("signature length is wrong")
- if len(pk) != b//8: raise Exception("public-key length is wrong")
- R = decodepoint(s[0:b//8])
- A = decodepoint(pk)
- S = decodeint(s[b//8:b//4])
- h = Hint(encodepoint(R) + pk + m)
- v1 = scalarmult(B,S)
-# v2 = edwards(R,scalarmult(A,h))
- v2 = pt_unxform(xpt_add(pt_xform(R), pt_xform(scalarmult(A, h))))
- return v1==v2
-
-##########################################################
-#
-# Curve25519 reference implementation by Matthew Dempsky, from:
-# http://cr.yp.to/highspeed/naclcrypto-20090310.pdf
-
-# P = 2 ** 255 - 19
-P = q
-A = 486662
-
-#def expmod(b, e, m):
-# if e == 0: return 1
-# t = expmod(b, e / 2, m) ** 2 % m
-# if e & 1: t = (t * b) % m
-# return t
-
-# def inv(x): return expmod(x, P - 2, P)
-
-def add(n, m, d):
- (xn, zn) = n
- (xm, zm) = m
- (xd, zd) = d
- x = 4 * (xm * xn - zm * zn) ** 2 * zd
- z = 4 * (xm * zn - zm * xn) ** 2 * xd
- return (x % P, z % P)
-
-def double(n):
- (xn, zn) = n
- x = (xn ** 2 - zn ** 2) ** 2
- z = 4 * xn * zn * (xn ** 2 + A * xn * zn + zn ** 2)
- return (x % P, z % P)
-
-def curve25519(n, base=9):
- one = (base,1)
- two = double(one)
- # f(m) evaluates to a tuple
- # containing the mth multiple and the
- # (m+1)th multiple of base.
- def f(m):
- if m == 1: return (one, two)
- (pm, pm1) = f(m // 2)
- if (m & 1):
- return (add(pm, pm1, one), double(pm1))
- return (double(pm), add(pm, pm1, one))
- ((x,z), _) = f(n)
- return (x * inv(z)) % P
-
-import random
-
-def genkey(n=0):
- n = n or random.randint(0,P)
- n &= ~7
- n &= ~(128 << 8 * 31)
- n |= 64 << 8 * 31
- return n
-
-#def str2int(s):
-# return int(hexlify(s), 16)
-# # return sum(ord(s[i]) << (8 * i) for i in range(32))
-#
-#def int2str(n):
-# return unhexlify("%x" % n)
-# # return ''.join([chr((n >> (8 * i)) & 255) for i in range(32)])
-
-#################################################
-
-def dsa_test():
- import os
- msg = str(random.randint(q,q+q)).encode('utf-8')
- sk = os.urandom(32)
- pk = publickey(sk)
- sig = signature(msg, sk, pk)
- return checkvalid(sig, msg, pk)
-
-def dh_test():
- sk1 = genkey()
- sk2 = genkey()
- return curve25519(sk1, curve25519(sk2)) == curve25519(sk2, curve25519(sk1))
-
diff --git a/wheel/signatures/ed25519py.py b/wheel/signatures/ed25519py.py
deleted file mode 100644
index 55eba2e..0000000
--- a/wheel/signatures/ed25519py.py
+++ /dev/null
@@ -1,52 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import warnings
-import os
-
-from collections import namedtuple
-from . import djbec
-
-__all__ = ['crypto_sign', 'crypto_sign_open', 'crypto_sign_keypair', 'Keypair',
- 'PUBLICKEYBYTES', 'SECRETKEYBYTES', 'SIGNATUREBYTES']
-
-PUBLICKEYBYTES=32
-SECRETKEYBYTES=64
-SIGNATUREBYTES=64
-
-Keypair = namedtuple('Keypair', ('vk', 'sk')) # verifying key, secret key
-
-def crypto_sign_keypair(seed=None):
- """Return (verifying, secret) key from a given seed, or os.urandom(32)"""
- if seed is None:
- seed = os.urandom(PUBLICKEYBYTES)
- else:
- warnings.warn("ed25519ll should choose random seed.",
- RuntimeWarning)
- if len(seed) != 32:
- raise ValueError("seed must be 32 random bytes or None.")
- skbytes = seed
- vkbytes = djbec.publickey(skbytes)
- return Keypair(vkbytes, skbytes+vkbytes)
-
-
-def crypto_sign(msg, sk):
- """Return signature+message given message and secret key.
- The signature is the first SIGNATUREBYTES bytes of the return value.
- A copy of msg is in the remainder."""
- if len(sk) != SECRETKEYBYTES:
- raise ValueError("Bad signing key length %d" % len(sk))
- vkbytes = sk[PUBLICKEYBYTES:]
- skbytes = sk[:PUBLICKEYBYTES]
- sig = djbec.signature(msg, skbytes, vkbytes)
- return sig + msg
-
-
-def crypto_sign_open(signed, vk):
- """Return message given signature+message and the verifying key."""
- if len(vk) != PUBLICKEYBYTES:
- raise ValueError("Bad verifying key length %d" % len(vk))
- rc = djbec.checkvalid(signed[:SIGNATUREBYTES], signed[SIGNATUREBYTES:], vk)
- if not rc:
- raise ValueError("rc != True", rc)
- return signed[SIGNATUREBYTES:]
-
diff --git a/wheel/signatures/keys.py b/wheel/signatures/keys.py
deleted file mode 100644
index 57d7feb..0000000
--- a/wheel/signatures/keys.py
+++ /dev/null
@@ -1,99 +0,0 @@
-"""Store and retrieve wheel signing / verifying keys.
-
-Given a scope (a package name, + meaning "all packages", or - meaning
-"no packages"), return a list of verifying keys that are trusted for that
-scope.
-
-Given a package name, return a list of (scope, key) suggested keys to sign
-that package (only the verifying keys; the private signing key is stored
-elsewhere).
-
-Keys here are represented as urlsafe_b64encoded strings with no padding.
-
-Tentative command line interface:
-
-# list trusts
-wheel trust
-# trust a particular key for all
-wheel trust + key
-# trust key for beaglevote
-wheel trust beaglevote key
-# stop trusting a key for all
-wheel untrust + key
-
-# generate a key pair
-wheel keygen
-
-# import a signing key from a file
-wheel import keyfile
-
-# export a signing key
-wheel export key
-"""
-
-import json
-import os.path
-from ..util import native, load_config_paths, save_config_path
-
-class WheelKeys(object):
- SCHEMA = 1
- CONFIG_NAME = 'wheel.json'
-
- def __init__(self):
- self.data = {'signers':[], 'verifiers':[]}
-
- def load(self):
- # XXX JSON is not a great database
- for path in load_config_paths('wheel'):
- conf = os.path.join(native(path), self.CONFIG_NAME)
- if os.path.exists(conf):
- with open(conf, 'r') as infile:
- self.data = json.load(infile)
- for x in ('signers', 'verifiers'):
- if not x in self.data:
- self.data[x] = []
- if 'schema' not in self.data:
- self.data['schema'] = self.SCHEMA
- elif self.data['schema'] != self.SCHEMA:
- raise ValueError(
- "Bad wheel.json version {0}, expected {1}".format(
- self.data['schema'], self.SCHEMA))
- break
- return self
-
- def save(self):
- # Try not to call this a very long time after load()
- path = save_config_path('wheel')
- conf = os.path.join(native(path), self.CONFIG_NAME)
- with open(conf, 'w+') as out:
- json.dump(self.data, out, indent=2)
- return self
-
- def trust(self, scope, vk):
- """Start trusting a particular key for given scope."""
- self.data['verifiers'].append({'scope':scope, 'vk':vk})
- return self
-
- def untrust(self, scope, vk):
- """Stop trusting a particular key for given scope."""
- self.data['verifiers'].remove({'scope':scope, 'vk':vk})
- return self
-
- def trusted(self, scope=None):
- """Return list of [(scope, trusted key), ...] for given scope."""
- trust = [(x['scope'], x['vk']) for x in self.data['verifiers'] if x['scope'] in (scope, '+')]
- trust.sort(key=lambda x: x[0])
- trust.reverse()
- return trust
-
- def signers(self, scope):
- """Return list of signing key(s)."""
- sign = [(x['scope'], x['vk']) for x in self.data['signers'] if x['scope'] in (scope, '+')]
- sign.sort(key=lambda x: x[0])
- sign.reverse()
- return sign
-
- def add_signer(self, scope, vk):
- """Remember verifying key vk as being valid for signing in scope."""
- self.data['signers'].append({'scope':scope, 'vk':vk})
-