Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Potential Buffer Overrun #470

Closed
mloit opened this issue Feb 15, 2022 · 0 comments · Fixed by #481
Closed

Potential Buffer Overrun #470

mloit opened this issue Feb 15, 2022 · 0 comments · Fixed by #481
Labels
ready for review issue is resolved, a final review is needed before closing

Comments

@mloit
Copy link
Contributor

mloit commented Feb 15, 2022

In both SECP256k1.swift and Data+Extension.swift there is a section of code to initialize a buffer with random data.

This is the excerpt form Data+Extension, though the code in SECP256k1 is virtually identical

    static func randomBytes(length: Int) -> Data? {
        for _ in 0...1024 {
            var data = Data(repeating: 0, count: length)
            let result = data.withUnsafeMutableBytes { (body: UnsafeMutableRawBufferPointer) -> Int32? in
                if let bodyAddress = body.baseAddress, body.count > 0 {
                    let pointer = bodyAddress.assumingMemoryBound(to: UInt8.self)
                    return SecRandomCopyBytes(kSecRandomDefault, 32, pointer)
                } else {
                    return nil
                }
            }
            if let notNilResult = result, notNilResult == errSecSuccess {
                return data
            }
        }
        return nil
    }

The problem is at the line of calling SecRandomCopyBytes, it is being called with a fixed count of 32, instead of the input length, as a result anything less that 32 bytes there will be an overrun beyond the end of the buffer, conversely if length > 32 only the first 32bytes will get initialized [while safe, probably not the desired outcome]

An overrun actually appears to happen with this code inside EthereumKeystoreV3 (perhaps other places as well)

   fileprivate func encryptDataToStorage(_ password: String, keyData: Data?, dkLen: Int = 32, N: Int = 4096, R: Int = 6, P: Int = 1, aesMode: String = "aes-128-cbc") throws {
        if (keyData == nil) {
            throw AbstractKeystoreError.encryptionError("Encryption without key data")
        }
        let saltLen = 32;
        guard let saltData = Data.randomBytes(length: saltLen) else {
            throw AbstractKeystoreError.noEntropyError
        }
        guard let derivedKey = scrypt(password: password, salt: saltData, length: dkLen, N: N, R: R, P: P) else {
            throw AbstractKeystoreError.keyDerivationError
        }
        let last16bytes = Data(derivedKey[(derivedKey.count - 16)...(derivedKey.count - 1)])
        let encryptionKey = Data(derivedKey[0...15])
        guard let IV = Data.randomBytes(length: 16) else {
            throw AbstractKeystoreError.noEntropyError
        }

.
.
.

in the above section, the 1st call to Data.randomBytes() is safe,as the buffer is 32 bytes, but on the 2nd call the buffer is only 16 (or at least the applicable portion), resuting in the 16bytes beyond the buffer being written to.

@ichikmarev ichikmarev added the ready for review issue is resolved, a final review is needed before closing label Mar 20, 2022
@yaroslavyaroslav yaroslavyaroslav mentioned this issue Mar 23, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ready for review issue is resolved, a final review is needed before closing
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants