Swift 对象的安全内存

Posted

技术标签:

【中文标题】Swift 对象的安全内存【英文标题】:Secure Memory For Swift Objects 【发布时间】:2015-02-27 05:45:31 【问题描述】:

我正在编写一个需要在内存中处理私钥的快速应用程序。由于此类对象的敏感性,在释放对象时需要清除键(也就是写入全零),并且内存无法分页到磁盘(通常使用 mlock() 完成)。

在 Objective-C 中,您可以提供自定义的 CFAllocator 对象,它允许您使用自己的函数来分配/解除分配/重新分配对象使用的内存。

因此,一种解决方案是在 Objective-c 中实现一个“SecureData”对象,该对象在内部使用自定义 CFAllocator(也在 Objective-c 中)创建一个 NSMutableData 对象。

但是,我有什么方法可以为纯 swift 对象(例如,结构或 [UInt8])提供我自己的自定义内存分配函数?或者有没有更好、“正确”的方式来快速实现这样的安全内存?

【问题讨论】:

你为什么不直接实现持有密钥并清除密钥的对象的 deinit 方法? @0x7fffffff – 你在寻找什么样的附加信息而不是我的回答?我会尝试添加它。 @AirspeedVelocity 你给出的答案已经很特别了。如果有的话,我只是在寻找一个额外的例子,或者如果可能的话,解释如何尝试解决与字符串和数组相关的问题。 (主要是字符串)感谢您的跟进。 @0x7fffffff 啊,明白了。谢谢。除了使用同样安全的自定义替代方案之外,我认为没有任何解决数组/字符串问题的方法,我会澄清这一点。我有一个等价的准系统数组,它分配自己的内存,我可以附加(虽然有很多代码)。 @AirspeedVelocity 也许是指向 gist/github 存储库的链接 【参考方案1】:

如果您想完全控制自己分配的内存区域,可以使用UnsafePointer 和 co:

// allocate enough memory for ten Ints
var ump = UnsafeMutablePointer<Int>.alloc(10)
// memory is in an uninitialized raw state

// initialize that memory with Int objects
// (here, from a collection)
ump.initializeFrom(reverse(0..<10))

// memory property gives you access to the underlying value
ump.memory // 9

// UnsafeMutablePointer acts like an IndexType
ump.successor().memory // 8
// and it has a subscript, but it's not a CollectionType
ump[3] // = 6

// wrap it in an UnsafeMutableBufferPointer to treat it
// like a collection (or UnsafeBufferPointer if you don't
// need to be able to alter the values)
let col = UnsafeMutableBufferPointer(start: ump, count: 10)
col[3] = 99
println(",".join(map(col,toString)))
// prints 9,8,7,99,5,4,3,2,1,0

ump.destroy(10)
// now the allocated memory is back in a raw state
// you could re-allocate it...
ump.initializeFrom(0..<10)
ump.destroy(10)

// when you're done, deallocate the memory
ump.dealloc(10)

您还可以让UnsafePointer 指向其他内存,例如您由某些 C API 提供的内存。

UnsafePointer 可以传递给 C 函数,这些函数接受一个指向连续内存块的指针。因此,出于您的目的,您可以将此指针传递给像 mlock 这样的函数:

let count = 10
let ump = UnsafeMutablePointer.allocate<Int>(count)
mlock(ump, UInt(sizeof(Int) * count))
// initialize, use, and destroy the memory
munlock(ump, UInt(sizeof(Int) * count))
ump.dealloc(count)

您甚至可以拥有自己的自定义类型:

struct MyStruct 
    let a: Int
    let b: Int


var pointerToStruct = UnsafeMutablePointer<MyStruct>.alloc(1)
pointerToStruct.initialize(MyStruct(a: 1, b: 2))
pointerToStruct.memory.b  // 2
pointerToStruct.destroy()
pointerToStruct.dealloc(1)

然而请注意,如果对类,甚至数组或字符串(或包含它们的结构)执行此操作,您将在内存中保存的只是指向这些对象的其他内存的指针分配和拥有。如果这对您很重要(例如,在您的示例中,您正在对此记忆做一些特殊的事情,例如保护它),那么这可能不是您想要的。

所以要么你需要使用固定大小的对象,要么进一步使用UnsafePointer 来保存指向更多内存区域的指针。如果他们不需要动态调整大小,那么只需分配一个不安全的指针,可能包装在一个集合接口的UnsafeBufferPointer 中,就可以做到这一点。

如果您需要更多动态行为,下面是一个可以根据需要调整大小的集合的非常简单的实现,可以对其进行增强以涵盖特殊的内存处理逻辑:

// Note this is a class not a struct, so it does NOT have value semantics,
// changing a copy changes all copies.
public class UnsafeCollection<T> 
    private var _len: Int = 0
    private var _buflen: Int = 0
    private var _buf: UnsafeMutablePointer<T> = nil

    public func removeAll(keepCapacity: Bool = false) 
        _buf.destroy(_len)
        _len = 0
        if !keepCapacity 
            _buf.dealloc(_buflen)
            _buflen = 0
            _buf = nil
        
    

    public required init()  
    deinit  self.removeAll(keepCapacity: false) 

    public var count: Int  return _len 
    public var isEmpty: Bool  return _len == 0 

为了满足MutableCollectionType 的要求(即CollectionType 加上可分配的下标):

extension UnsafeCollection: MutableCollectionType 
    typealias Index = Int
    public var startIndex: Int  return 0 
    public var endIndex: Int  return _len 

    public subscript(idx: Int) -> T 
        get 
            precondition(idx < _len)
            return _buf[idx]
        
        set(newElement) 
            precondition(idx < _len)
            let ptr = _buf.advancedBy(idx)
            ptr.destroy()
            ptr.initialize(newElement)
        
    

    typealias Generator = IndexingGenerator<UnsafeCollection>
    public func generate() -> Generator 
        return Generator(self)
    

还有ExtensibleCollectionType,以实现动态增长:

extension UnsafeCollection: ExtensibleCollectionType 
    public func reserveCapacity(n: Index.Distance) 
        if n > _buflen 
            let newBuf = UnsafeMutablePointer<T>.alloc(n)
            newBuf.moveInitializeBackwardFrom(_buf, count: _len)
            _buf.dealloc(_buflen)
            _buf = newBuf
            _buflen = n
        
    

    public func append(x: T) 
        if _len == _buflen 
            reserveCapacity(Int(Double(_len) * 1.6) + 1)
        
        _buf.advancedBy(_len++).initialize(x)
    

    public func extend<S: SequenceType where S.Generator.Element == T>
      (newElements: S) 
        var g = newElements.generate()
        while let x: T = g.next() 
            self.append(x)
        
    

【讨论】:

谢谢!这似乎已经取得了大部分进展。内存是否分配在一个连续的块中?例如,使用以下代码: let myPtr = UnsafeMutablePointer.alloc(10) 然后我会执行 mlock(myPtr, 10) 之类的操作来防止内存被分页到磁盘。 我相信是这样(也意味着你可以将它作为类似数组指针的对象传递给 C 函数)【参考方案2】:

我知道这个问题很老了,但对于那些来到这里的人来说有些问题:从 ios 10 开始,您可以使用 Secure Enclave 安全地存储私钥。它的工作方式是所有需要解密的操作都在 Secure Enclave 内执行,因此您不必担心类的运行时挂钩或内存泄漏。

看这里:https://developer.apple.com/documentation/security/certificate_key_and_trust_services/keys/storing_keys_in_the_secure_enclave

【讨论】:

以上是关于Swift 对象的安全内存的主要内容,如果未能解决你的问题,请参考以下文章

Swift学习笔记-内存安全性

swift实现线程安全的栈和队列

Swift 中昂贵的对象管理

Swift COW 线程安全

C++ 之父:Rust等内存安全语言的安全性并不优于C++

内存线程安全与并发