-
Notifications
You must be signed in to change notification settings - Fork 155
/
Block Function.swift
143 lines (120 loc) · 5.78 KB
/
Block Function.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftCrypto open source project
//
// Copyright (c) 2023 Apple Inc. and the SwiftCrypto project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.md for the list of SwiftCrypto project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
import Crypto
@_implementationOnly import CCryptoBoringSSL
@_implementationOnly import CCryptoBoringSSLShims
@_implementationOnly import CryptoBoringWrapper
import Foundation
extension AES {
private static let blockSize = 128 / 8
/// Apply the AES permutation operation in the encryption direction.
///
/// This function applies the core AES block operation to `payload` in the encryption direction. Note that this is
/// not performing any kind of block cipher mode, and does not authenticate the payload. This is a dangerous primitive
/// that should only be used to compose higher-level primitives, and should not be used directly.
///
/// - parameter payload: The payload to encrypt. Must be exactly 16 bytes long.
/// - parameter key: The encryption key to use.
/// - throws: On invalid parameter sizes.
public static func permute<Payload: MutableCollection>(_ payload: inout Payload, key: SymmetricKey) throws where Payload.Element == UInt8 {
return try Self.permuteBlock(&payload, key: key, permutation: .forward)
}
/// Apply the AES permutation operation in the decryption direction.
///
/// This function applies the core AES block operation to `payload` in the decryption direction. Note that this is
/// not performing any kind of block cipher mode, and does not authenticate the payload. This is a dangerous primitive
/// that should only be used to compose higher-level primitives, and should not be used directly.
///
/// - parameter payload: The payload to decrypt. Must be exactly 16 bytes long.
/// - parameter key: The decryption key to use.
/// - throws: On invalid parameter sizes.
public static func inversePermute<Payload: MutableCollection>(_ payload: inout Payload, key: SymmetricKey) throws where Payload.Element == UInt8 {
return try Self.permuteBlock(&payload, key: key, permutation: .backward)
}
private static func permuteBlock<Payload: MutableCollection>(_ payload: inout Payload, key: SymmetricKey, permutation: Permutation) throws where Payload.Element == UInt8 {
if payload.count != Int(Self.blockSize) {
throw CryptoKitError.incorrectParameterSize
}
if !AES.isValidKey(key) {
throw CryptoKitError.incorrectKeySize
}
let requiresSlowPath: Bool = try payload.withContiguousMutableStorageIfAvailable { storage in
try Self.permute(UnsafeMutableRawBufferPointer(storage), key: key, permutation: permutation)
return false
} ?? true
if requiresSlowPath {
try AES.Block.withStackStorage { blockBytes in
precondition(blockBytes.count == payload.count)
blockBytes.copyBytes(from: payload)
try Self.permute(blockBytes, key: key, permutation: permutation)
var index = payload.startIndex
for byte in blockBytes {
payload[index] = byte
payload.formIndex(after: &index)
}
}
}
}
enum Permutation {
case forward
case backward
}
private static func permute(_ payload: UnsafeMutableRawBufferPointer, key: SymmetricKey, permutation: Permutation) throws {
precondition(AES.isValidKey(key))
precondition(payload.count == Int(Self.blockSize))
key.withUnsafeBytes { keyPtr in
// We bind both pointers here. These binds are not technically safe, but because we
// know the pointers don't persist they can't violate the aliasing rules. We really
// want a "with memory rebound" function but we don't have it yet.
let keyBytes = keyPtr.bindMemory(to: UInt8.self)
let blockBytes = payload.bindMemory(to: UInt8.self)
var key = AES_KEY()
if permutation == .forward {
let rc = CCryptoBoringSSL_AES_set_encrypt_key(keyBytes.baseAddress, UInt32(keyBytes.count * 8), &key)
precondition(rc == 0)
CCryptoBoringSSL_AES_encrypt(blockBytes.baseAddress, blockBytes.baseAddress, &key)
} else {
let rc = CCryptoBoringSSL_AES_set_decrypt_key(keyBytes.baseAddress, UInt32(keyBytes.count * 8), &key)
precondition(rc == 0)
CCryptoBoringSSL_AES_decrypt(blockBytes.baseAddress, blockBytes.baseAddress, &key)
}
}
}
private struct Block {
private var storage: (UInt64, UInt64)
private init() {
assert(MemoryLayout<Self>.size == Int(AES.blockSize))
self.storage = (0, 0)
}
private mutating func withUnsafeMutableBytes<ReturnType>(
_ body: (UnsafeMutableRawBufferPointer) throws -> ReturnType
) rethrows -> ReturnType {
return try Swift.withUnsafeMutableBytes(of: &self.storage, body)
}
static func withStackStorage<ReturnType>(
_ body: (UnsafeMutableRawBufferPointer) throws -> ReturnType
) rethrows -> ReturnType {
var storage = Self()
return try storage.withUnsafeMutableBytes(body)
}
}
private static func isValidKey(_ key: SymmetricKey) -> Bool {
switch key.bitCount {
case 128, 192, 256:
return true
default:
return false
}
}
}