Skip to content

Instantly share code, notes, and snippets.

@yllan
Created August 17, 2018 09:48
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yllan/413ae0d4b17dd6b47383e6a46da55cdd to your computer and use it in GitHub Desktop.
Save yllan/413ae0d4b17dd6b47383e6a46da55cdd to your computer and use it in GitHub Desktop.
//
// Signer.swift
// Sign
//
// Created by Yung-Luen Lan on 2018/8/17.
// Copyright © 2018 yllan. All rights reserved.
//
import Foundation
func der(fromPEM: String) -> Data {
let base64 = fromPEM.split(separator: "\n").filter({ !$0.hasPrefix("-----") }).joined(separator: "")
return Data(base64Encoded: base64)!
}
indirect enum Element {
case seq(elements: [Element])
case integer(int: Int)
case bytes(data: Data)
case constructed(tag: Int, elem: Element)
case unknow
}
func readLength(data: Data) -> (Int, Int) {
if data[0] & 0x80 == 0x00 { // short form
return (Int(data[0]), 1)
} else {
let lenghOfLength = Int(data[0] & 0x7F)
var result: Int = 0
for i in 1..<(1 + lenghOfLength) {
result = 256 * result + Int(data[i])
}
return (result, 1 + lenghOfLength)
}
}
func parse(data: Data) -> (Element, Int) {
guard data.count >= 2 else {
// format error
return (.unknow, data.count)
}
switch data[0] {
case 0x30: // sequence
let (length, lengthOfLength) = readLength(data: data.advanced(by: 1))
var result: [Element] = []
var subdata = data.advanced(by: 1 + lengthOfLength)
var alreadyRead = 0
while alreadyRead < length {
let (e, l) = parse(data: subdata)
result.append(e)
subdata = subdata.count > l ? subdata.advanced(by: l) : Data()
alreadyRead += l
}
return (.seq(elements: result), 1 + lengthOfLength + length)
case 0x02: // integer
let (length, lengthOfLength) = readLength(data: data.advanced(by: 1))
var result: Int = 0
let subdata = data.advanced(by: 1 + lengthOfLength)
// ignore negative case!
for i in 0..<length {
result = 256 * result + Int(subdata[i])
}
return (.integer(int: result), 1 + lengthOfLength + length)
case let s where (s & 0xe0) == 0xa0: // constructed
let tag = Int(s & 0x1f)
let (length, lengthOfLength) = readLength(data: data.advanced(by: 1))
let subdata = data.advanced(by: 1 + lengthOfLength)
let (e, l) = parse(data: subdata)
return (.constructed(tag: tag, elem: e), 1 + lengthOfLength + length)
default: // octet string
let (length, lengthOfLength) = readLength(data: data.advanced(by: 1))
return (.bytes(data: data.subdata(in: (1+lengthOfLength)..<(1+lengthOfLength+length))), 1 + lengthOfLength + length)
}
}
func test() {
let sign_key_pem = """
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIDHrPrpPriicRqwmF7fNx35v9t9LzHf7yUdo9LowETFBoAoGCCqGSM49
AwEHoUQDQgAEHb7+UQvaVjKVjPkXksfBWQPoyPDXVjcwJ863DP2zwPnZ6YWmVoig
W/HowZS47aNZ1kWNGt4O2MVs/nRHvi7Xgg==
-----END EC PRIVATE KEY-----
"""
let (result, _) = parse(data: der(fromPEM: sign_key_pem))
let msg: Data = Data(repeating: 0xA0, count: 1000)
if case let Element.seq(elements: es) = result,
case let Element.bytes(data: privateData) = es[1],
case let Element.constructed(tag: _, elem: e) = es[3],
case let Element.bytes(data: publicData) = e
{
let d = (publicData.drop(while: { $0 == 0x00}) + privateData)
var error: Unmanaged<CFError>? = nil
let key = SecKeyCreateWithData(d as CFData,
[kSecAttrKeyType: kSecAttrKeyTypeEC,
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
kSecAttrKeySizeInBits: d.count * 8] as CFDictionary,
&error)
var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
CC_SHA256((msg as NSData).bytes, CC_LONG(msg.count), &hash)
let digest = Data(bytes: hash)
if let sig = SecKeyCreateSignature(key!, SecKeyAlgorithm.ecdsaSignatureDigestX962SHA256, digest as CFData, &error) {
print(sig)
} else {
print(error!.takeRetainedValue())
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment