+ # The first field is keytype again.
+ if subfields[0] != sshkeytype:
+ raise KeyFormatError("""
+ outer and embedded key types do not match: '%s', '%s'
+ """ % (sshkeytype, subfields[1]))
+
+ # Translate key type string into something PuTTY can use, and
+ # munge the rest of the data.
+ if sshkeytype == "ssh-rsa":
+ keytype = "rsa2"
+ # The rest of the subfields we can treat as an opaque list
+ # of bignums (same numbers and order as stored by PuTTY).
+ keyparams = map (strtolong, subfields[1:])
+
+ elif sshkeytype == "ssh-dss":
+ keytype = "dss"
+ # Same again.
+ keyparams = map (strtolong, subfields[1:])
+
+ elif sshkeytype == "ecdsa-sha2-nistp256" \
+ or sshkeytype == "ecdsa-sha2-nistp384" \
+ or sshkeytype == "ecdsa-sha2-nistp521":
+ keytype = sshkeytype
+ # Have to parse this a bit.
+ if len(subfields) > 3:
+ raise KeyFormatError("too many subfields in blob")
+ (curvename, Q) = subfields[1:]
+ # First is yet another copy of the key name.
+ if not re.match("ecdsa-sha2-" + re.escape(curvename),
+ sshkeytype):
+ raise KeyFormatError("key type mismatch ('%s' vs '%s')"
+ % (sshkeytype, curvename))
+ # Second contains key material X and Y (hopefully).
+ # First a magic octet indicating point compression.
+ if struct.unpack("B", Q[0])[0] != 4:
+ # No-one seems to use this.
+ raise KeyFormatError("can't convert point-compressed ECDSA")
+ # Then two equal-length bignums (X and Y).
+ bnlen = len(Q)-1
+ if (bnlen % 1) != 0:
+ raise KeyFormatError("odd-length X+Y")
+ bnlen = bnlen / 2
+ (x,y) = Q[1:bnlen+1], Q[bnlen+1:2*bnlen+1]
+ keyparams = [curvename] + map (strtolong, [x,y])
+
+ elif sshkeytype == "ssh-ed25519":
+ keytype = sshkeytype
+
+ if len(subfields) != 2:
+ raise KeyFormatError("wrong number of subfields in blob")
+ if subfields[0] != sshkeytype:
+ raise KeyFormatError("key type mismatch ('%s' vs '%s')"
+ % (sshkeytype, subfields[0]))
+ # Key material y, with the top bit being repurposed as
+ # the expected parity of the associated x (point
+ # compression).
+ y = strtolong_le(subfields[1])
+ x_parity = y >> 255
+ y &= ~(1 << 255)
+
+ # Standard Ed25519 parameters.
+ p = 2**255 - 19
+ d = 0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3
+
+ # Recover x^2 = (y^2 - 1) / (d y^2 + 1).
+ #
+ # With no real time constraints here, it's easier to
+ # take the inverse of the denominator by raising it to
+ # the power p-2 (by Fermat's Little Theorem) than
+ # faffing about with the properly efficient Euclid
+ # method.
+ xx = (y*y - 1) * pow(d*y*y + 1, p-2, p) % p
+
+ # Take the square root, which may require trying twice.
+ x = pow(xx, (p+3)/8, p)
+ if pow(x, 2, p) != xx:
+ x = x * pow(2, (p-1)/4, p) % p
+ assert pow(x, 2, p) == xx
+
+ # Pick the square root of the correct parity.
+ if (x % 2) != x_parity:
+ x = p - x