Permalink
Browse files

single certificate getter (non-recursive)

  • Loading branch information...
1 parent 5906f5f commit 8ebd0ad97b89f5835c22287ffaebc06416f5b50e Attila Oláh committed Mar 28, 2013
Showing with 143 additions and 22 deletions.
  1. +1 −1 api/batch.py
  2. +31 −0 api/cert.py
  3. +10 −3 api/ds.py
  4. +3 −1 app.yaml
  5. +21 −0 prcert/__init__.py
  6. +73 −0 prcert/extract.py
  7. +4 −17 prcert/verify.py
View
@@ -71,5 +71,5 @@ def put(self, batch_hash):
app = webapp.WSGIApplication([
(r'^/batch/([0-9a-f]{40})$', BatchUpload),
- (r'^/batch/([0-9a-f]{40})(?:.(bz2|gz|z))?$', Batch),
+ (r'^/batch/([0-9a-f]{40})(?:\.(bz2|gz|z))?$', Batch),
])
View
@@ -0,0 +1,31 @@
+# -*- coding: utf-8 -*-
+
+from google.appengine.ext import ndb
+from google.appengine.ext import webapp
+
+from prcert import get_batch_hash
+
+from . import ds
+
+
+class Cert(webapp.RequestHandler):
+ """Single certificate request handler."""
+
+ @ndb.synctasklet
+ def get(self, prime, suffix=''):
+ """Redirect to the batch of certs, if available."""
+ prime = long(prime)
+ entity = yield ds.batch_key(get_batch_hash(prime)).get_async()
+ if entity is None:
+ # Certificate not found:
+ self.response.out.write(u'Certificate not found.')
+ self.response.status = 404
+ raise ndb.Return()
+ cert_data = entity.get_cert(prime, suffix or 'raw')
+ #self.response.content_type = b'application/octet-stream'
+ self.response.out.write(cert_data)
+
+
+app = webapp.WSGIApplication([
+ (r'^/cert/(\d+)(?:\.(json|xml|txt))?$', Cert),
+])
View
@@ -1,10 +1,13 @@
# -*- coding: utf-8 -*-
-import bz2
-
from google.appengine.ext import blobstore
from google.appengine.ext import ndb
+from prcert import compress
+from prcert import extract
+
+
+
COMPRESS = True
@@ -23,9 +26,13 @@ def read(self, decompress=True):
with self.open() as blob:
data = blob.read()
if decompress:
- data = bz2.decompress(data)
+ data = compress.decompress(data)
return data
+ def get_cert(self, prime, format=None):
+ """Extract a single certificate."""
+ return extract.extract_cert(self.read(), prime, format=format)
+
def batch_key(batch_hash):
"""Generate a batch key."""
View
@@ -17,5 +17,7 @@ libraries:
handlers:
-- url: /batch/([a-z0-9]{40})(.(bz2|gz|z))?
+- url: /cert/(\d+)(\.(json|xml|txt))?
+ script: api.cert.app
+- url: /batch/([a-z0-9]{40})(\.(bz2|gz|z))?
script: api.batch.app
View
@@ -1 +1,22 @@
# -*- coding: utf-8 -*-
+
+import hashlib
+
+from . import varint
+
+
+NULL = b'\x00'
+
+BATCH_SIZE = 16
+
+CYPHER = hashlib.sha1
+
+
+def get_hash(n):
+ """Return the hash for the given number."""
+ return CYPHER(varint.uencode(n)).hexdigest()
+
+
+def get_batch_hash(n, b_size=BATCH_SIZE):
+ """Return the batch hash for the given number."""
+ return get_hash(n>>b_size)
View
@@ -0,0 +1,73 @@
+# -*- coding: utf-8 -*-
+
+import json
+import operator
+
+from . import NULL
+from . import varint
+
+
+AVAILABLE_FORMATS = None, 'json', 'xml', 'txt', 'raw'
+
+XML_TEMPLATE = b'''\
+<?xml version="1.0"?>
+<certificate>
+ <factors>
+{}
+ </factors>
+ <witness>{}</witness>
+</certificate>
+'''
+XML_FACTOR_TEMPLATE = b'''\
+ <factor>
+ <base>{}</base>
+ <exponent>{}</exponent>
+ </factor>\
+'''
+
+
+def extract_cert(batch, prime, format=None):
+ """Extract a single cetrificate from a batch."""
+ # Decode and verify the certificate:
+ current_cert = b''
+ for part in batch:
+ if part != NULL:
+ current_cert += part
+ else:
+ current_prime, witness, pairs = get_prime(current_cert)
+ if current_prime == prime:
+ # Certificate found:
+ if format is None:
+ # No formatting, return native values:
+ return witness, pairs
+ if format == 'json':
+ return json.dumps({
+ 'witness': witness,
+ 'factors': [{
+ 'base': base,
+ 'exponent': exponent,
+ } for base, exponent in pairs]}, indent=2, sort_keys=True)
+ if format == 'xml':
+ return XML_TEMPLATE.format(
+ b'\n'.join(XML_FACTOR_TEMPLATE.format(*pair) for pair in pairs), witness)
+ if format == 'txt':
+ return ValueError (
+ bytes(witness) +
+ ' ' +
+ ' '.join('{} {}'.format(*pair) for pair in pairs)
+ )
+ if format == 'raw':
+ return current_cert
+ raise ValueError(u'Format must be one of {!r}, not {!r}.'.format(AVAILABLE_FORMATS, format))
+ if current_prime > prime:
+ # Certificate not in batch:
+ return
+ current_cert = b''
+
+
+def get_prime(data):
+ """Get the prime for a single certificate."""
+ # Unpack the certificate:
+ witness, pairs = varint.decode_cert(data)
+ prime = reduce(operator.mul, map(pow, *zip(*pairs))) + 1
+ return prime, witness, pairs
View
@@ -1,29 +1,16 @@
# -*- coding: utf-8 -*-
-import hashlib
import math
+from . import BATCH_SIZE
+from . import NULL
from . import compress
+from . import get_batch_hash
from . import mesq
from . import varint
from . import small_primes
-NULL = b'\x00'
-
-BATCH_SIZE = 16
-CYPHER = hashlib.sha1
-
-
-class Error(BaseException):
- """Verification error."""
-
-
-def _b_hash(prime, b_size=BATCH_SIZE):
- """Return the batch name for the prime."""
- return CYPHER(varint.uencode(prime>>b_size)).hexdigest()
-
-
def _factors(n):
"""Naive factorization algorithm, but good enough for witnesses."""
limit, check, num = int(math.sqrt(n)) + 1, 2, n
@@ -67,7 +54,7 @@ def batch(data, b_hash, b_size=BATCH_SIZE, recompress=None):
if min_prime>>b_size != max_prime>>b_size:
return False
# Make sure the batch name is correct:
- if _b_hash(min_prime, b_size) != b_hash:
+ if get_batch_hash(min_prime, b_size) != b_hash:
return False
# Looks like the batch is valid.
if recompress:

0 comments on commit 8ebd0ad

Please sign in to comment.