-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from swift-libp2p/dev/pem+api
PEM Imports and Exports + Code Refactoring
- Loading branch information
Showing
9 changed files
with
413 additions
and
226 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// | ||
// PeerID+Equatable.swift | ||
// | ||
// | ||
// Created by Brandon Toms on 9/23/22. | ||
// | ||
|
||
import Foundation | ||
|
||
extension PeerID:Equatable { | ||
public static func == (lhs: PeerID, rhs: PeerID) -> Bool { | ||
lhs.id == rhs.id | ||
} | ||
public static func == (lhs: [UInt8], rhs: PeerID) -> Bool { | ||
lhs == rhs.id | ||
} | ||
public static func == (lhs: Data, rhs: PeerID) -> Bool { | ||
lhs.bytes == rhs.id | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
// | ||
// PeerID+JSON.swift | ||
// | ||
// | ||
// Created by Brandon Toms on 9/23/22. | ||
// | ||
|
||
import LibP2PCrypto | ||
import Foundation | ||
import Multihash | ||
|
||
/// - MARK: JSON Imports and Exports | ||
public extension PeerID { | ||
/// An Internal PeerID struct to facilitate JSON Encoding and Decoding | ||
internal struct PeerIDJSON:Codable { | ||
/// base58 encoded string | ||
let id:String | ||
/// base64 encoded publicKey protobuf | ||
let pubKey:String? | ||
/// base64 encoded privateKey protobuf | ||
let privKey:String? | ||
} | ||
|
||
/// Initialize a PeerID from JSON data | ||
/// | ||
/// Expects a JSON object of the form | ||
/// ``` | ||
/// { | ||
/// obj.id: String - The multihash encoded in base58 | ||
/// obj.pubKey: String? - The public key in protobuf format, encoded in 'base64' | ||
/// obj.privKey: String? - The private key in protobuf format, encoded in 'base64' | ||
/// } | ||
/// ``` | ||
convenience init(fromJSON json:Data) throws { | ||
let data = try JSONDecoder().decode(PeerIDJSON.self, from: json) | ||
|
||
if data.privKey == nil && data.pubKey == nil { | ||
/// Only ID Present... | ||
try self.init(fromBytesID: Multihash(b58String: data.id).value) | ||
} else if data.privKey == nil, let pubKey = data.pubKey { | ||
/// Only Public Key and ID Present, lets init via the public key and derive the ID | ||
/// TODO: Compare the provided ID and the Derived ID and throw an error if they dont match... | ||
try self.init(marshaledPublicKey: pubKey, base: .base64) | ||
} else if let privKey = data.privKey { | ||
/// Private Key was provided. Lets init via the private key and derive both the public key and the ID | ||
/// TODO: Compare the provided publicKey and ID to the ones derived from the private key and throw an error if they don't match... | ||
try self.init(marshaledPrivateKey: privKey, base: .base64) | ||
} else { | ||
throw NSError(domain: "Failed to init PeerID from json", code: 0, userInfo: nil) | ||
} | ||
} | ||
|
||
/// Exports our PeerID as a JSON object | ||
/// | ||
/// Returns a JSON object of the form | ||
/// ``` | ||
/// { | ||
/// id: String - The multihash encoded in base58 | ||
/// pubKey: String? - The public key in protobuf format, encoded in 'base64' | ||
/// privKey: String? - The private key in protobuf format, encoded in 'base64' | ||
/// } | ||
/// ``` | ||
func toJSON(includingPrivateKey:Bool = false) throws -> Data { | ||
let pidJSON = PeerIDJSON( | ||
id: self.b58String, | ||
pubKey: try? self.keyPair?.publicKey.marshal().asString(base: .base64), | ||
privKey: includingPrivateKey ? try? self.keyPair?.privateKey?.marshal().asString(base: .base64) : nil | ||
) | ||
|
||
return try JSONEncoder().encode(pidJSON) | ||
} | ||
|
||
/// Exports our PeerID as a JSON object | ||
/// | ||
/// Returns a JSON object as a String | ||
/// ``` | ||
/// { | ||
/// id: String - The multihash encoded in base58 | ||
/// pubKey: String? - The public key in protobuf format, encoded in 'base64' | ||
/// privKey: String? - The private key in protobuf format, encoded in 'base64' | ||
/// } | ||
/// ``` | ||
func toJSONString(includingPrivateKey:Bool = false) throws -> String? { | ||
return try String(data: self.toJSON(), encoding: .utf8) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
// | ||
// PeerID+Marshaled.swift | ||
// | ||
// | ||
// Created by Brandon Toms on 9/23/22. | ||
// | ||
|
||
import LibP2PCrypto | ||
import Foundation | ||
import Multibase | ||
|
||
/// - MARK: Marshaled Imports and Exports | ||
public extension PeerID { | ||
/// Inits a `PeerID` from a marshaled `PeerID` string | ||
/// - Note: `base` can be left `nil` if the marshaledPeerID String is `Multibase` compliant (includes the multibase prefix) otherwise, you must specify the ecoded base of the string... | ||
convenience init(marshaledPeerID:String, base: BaseEncoding? = nil) throws { | ||
let marshaledData:Data | ||
if let base = base { | ||
marshaledData = try BaseEncoding.decode(marshaledPeerID, as: base).data | ||
} else { | ||
marshaledData = try BaseEncoding.decode(marshaledPeerID).data | ||
} | ||
try self.init(marshaledPeerID: marshaledData) | ||
} | ||
|
||
/// Inits a `PeerID` from a marshaled `PeerID` | ||
convenience init(marshaledPeerID data:Data) throws { | ||
// Attampt to instantiate a PeerIdProto with the raw, marshaled, data | ||
let protoPeerID = try PeerIdProto(contiguousBytes: data) | ||
|
||
//print(protoPeerID.id.asString(base: .base64)) | ||
//print("Has PubKey: \(protoPeerID.hasPubKey)") | ||
//print(protoPeerID.pubKey.asString(base: .base64)) | ||
//print("Has PrivKey: \(protoPeerID.hasPrivKey)") | ||
//print(protoPeerID.privKey.asString(base: .base64)) | ||
|
||
// Enusre the Marshaled data included at least a public key (is this necessary, would we ever need to unmarshal an ID only?) | ||
guard protoPeerID.hasPubKey || protoPeerID.hasPrivKey else { | ||
throw NSError(domain: "No Public or Private Key Found in marshaled data", code: 0, userInfo: nil) | ||
} | ||
|
||
// If we have a private key, attempt to instantiate the PeerID via the private key, otherwise, try the public key... | ||
if protoPeerID.hasPrivKey { | ||
try self.init(marshaledPrivateKey: protoPeerID.privKey) | ||
} else if protoPeerID.hasPubKey { | ||
try self.init(marshaledPublicKey: protoPeerID.pubKey) | ||
} else { | ||
throw NSError(domain: "No Public or Private Key Found in marshaled data", code: 0, userInfo: nil) | ||
} | ||
} | ||
|
||
/// Inits a `PeerID` from a marshaled public key string | ||
convenience init(marshaledPublicKey str:String, base:BaseEncoding) throws { | ||
try self.init(keyPair: LibP2PCrypto.Keys.KeyPair(marshaledPublicKey: str, base: base)) | ||
} | ||
|
||
/// Inits a `PeerID` from a marshaled public key | ||
convenience init(marshaledPublicKey key:Data) throws { | ||
try self.init(keyPair: LibP2PCrypto.Keys.KeyPair(marshaledPublicKey: key)) | ||
} | ||
|
||
/// Inits a `PeerID` from a marshaled private key string | ||
convenience init(marshaledPrivateKey str:String, base:BaseEncoding) throws { | ||
try self.init(keyPair: LibP2PCrypto.Keys.KeyPair(marshaledPrivateKey: str, base: base)) | ||
} | ||
|
||
/// Inits a `PeerID` from a marshaled private key | ||
convenience init(marshaledPrivateKey data:Data) throws { | ||
try self.init(keyPair: LibP2PCrypto.Keys.KeyPair(marshaledPrivateKey: data)) | ||
} | ||
|
||
|
||
// private static func computeDigest(pubKey:SecKey) throws -> [UInt8] { | ||
// let bytes = try pubKey.rawRepresentation() | ||
// return try self.computeDigest(rawPubKey: bytes) | ||
// } | ||
// | ||
// /// - Note: We need to marshal the raw public key before multihashing it.... | ||
// private static func computeDigest(rawPubKey bytes:Data) throws -> [UInt8] { | ||
// let marshaled = try LibP2PCrypto.Keys.marshalPublicKey(raw: bytes, keyType: .RSA(bits: .B1024)) | ||
// //print(marshaled.asString(base: .base64Pad)) | ||
// if marshaled.count <= 42 { | ||
// return try Multihash(raw: marshaled, hashedWith: .identity).value | ||
// } else { | ||
// //let mh = try Multihash(raw: bytes, hashedWith: .sha2_256) | ||
// //print("Value: \(mh.value.asString(base: .base16))") | ||
// //print("Hex: \(mh.hexString)") | ||
// //print("Digest: \(mh.digest?.asString(base: .base16) ?? "NIL")") | ||
// return try Multihash(raw: marshaled, hashedWith: .sha2_256).value //pubKey.hash() | ||
// } | ||
// } | ||
|
||
|
||
/// Returns a protocol-buffers encoded version of the id, public key and, if `includingPrivateKey` is set to `true`, the private key. | ||
func marshal(includingPrivateKey:Bool = false) throws -> [UInt8] { | ||
var pid = PeerIdProto() | ||
pid.id = Data(self.id) | ||
pid.pubKey = try self.keyPair?.publicKey.marshal() ?? Data() | ||
if includingPrivateKey, let privKey = self.keyPair?.privateKey { | ||
pid.privKey = try privKey.marshal() | ||
} | ||
return try pid.serializedData().bytes | ||
} | ||
|
||
func marshalPrivateKey() throws -> [UInt8] { | ||
guard let privKey = self.keyPair?.privateKey else { | ||
throw NSError(domain: "This PeerID doesn't have a Private Key to Marshal", code: 0, userInfo: nil) | ||
} | ||
return try privKey.marshal().bytes | ||
} | ||
|
||
func marshalPublicKey() throws -> [UInt8] { | ||
guard let pubKey = self.keyPair?.publicKey else { | ||
throw NSError(domain: "This PeerID doesn't have a Public Key to Marshal", code: 0, userInfo: nil) | ||
} | ||
return try pubKey.marshal().bytes | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
// | ||
// PeerID+PEM.swift | ||
// | ||
// | ||
// Created by Brandon Toms on 9/23/22. | ||
// | ||
|
||
import LibP2PCrypto | ||
import Foundation | ||
|
||
/// - MARK: PEM Imports and Exports | ||
public extension PeerID { | ||
convenience init(pem: String, password: String?) throws { | ||
try self.init(keyPair: LibP2PCrypto.Keys.KeyPair(pem: pem, password: password)) | ||
} | ||
|
||
enum ExportType { | ||
case publicPEMString | ||
case privatePEMString(encryptedWithPassword:String) | ||
case unencrypredPrivatePEMString | ||
} | ||
|
||
/// Exports the KeyPair as PEM structured String. Private Keys can be encrypted with a password before export. | ||
func exportKeyPair(as exportType:ExportType) throws -> String { | ||
guard let keyPair = self.keyPair else { throw NSError(domain: "No Underlying Key Pair to Export", code: 0, userInfo: nil) } | ||
switch exportType { | ||
case .publicPEMString: | ||
return try keyPair.publicKey.exportPublicKeyPEMString(withHeaderAndFooter: true) | ||
case .unencrypredPrivatePEMString: | ||
guard keyPair.privateKey != nil else { throw NSError(domain: "No Private Key to Export", code: 0, userInfo: nil) } | ||
return try keyPair.exportPrivatePEMString(withHeaderAndFooter: true) | ||
case .privatePEMString(let password): | ||
guard !password.isEmpty else { throw NSError(domain: "Password shouldn't be empty", code: 0, userInfo: nil) } | ||
return try keyPair.exportEncryptedPrivatePEMString(withPassword: password) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// | ||
// PeerID+Signature.swift | ||
// | ||
// | ||
// Created by Brandon Toms on 9/23/22. | ||
// | ||
|
||
import LibP2PCrypto | ||
import Foundation | ||
|
||
/// - MARK: PeerID Signatures and Verification Methods | ||
public extension PeerID { | ||
// Signs data using this PeerID's private key. This signature can then be verified by a remote peer using this PeerID's public key | ||
func signature(for msg:Data) throws -> Data { | ||
guard let priv = keyPair?.privateKey else { | ||
throw NSError(domain: "A private key is required for generating signature and this PeerID doesn't contain a private key.", code: 0, userInfo: nil) | ||
} | ||
|
||
return try priv.sign(message: msg) | ||
} | ||
|
||
// Using this PeerID's public key, this method checks to see if the signature data was in fact signed by this peer and is a valid signature for the expected data | ||
func isValidSignature(_ signature:Data, for expectedData:Data) throws -> Bool { | ||
guard let pub = keyPair?.publicKey else { | ||
throw NSError(domain: "A public key is required for verifying signatures and this PeerID doesn't contain a public key.", code: 0, userInfo: nil) | ||
} | ||
|
||
return try pub.verify(signature: signature, for: expectedData) | ||
} | ||
} |
Oops, something went wrong.