First pass to make the code a tiny bit more consistent
This commit is contained in:
parent
79ac24af27
commit
494716a51d
11
README.md
11
README.md
|
@ -144,8 +144,7 @@ let stream_enc = sodium.secretStream.xchacha20poly1305.initPush(secretKey: secre
|
|||
let header = stream_enc.header()
|
||||
let encrypted1 = stream_enc.push(message: message1)!
|
||||
let encrypted2 = stream_enc.push(message: message2)!
|
||||
let encrypted3 = stream_enc.push(message: message3,
|
||||
tag: SecretStream.XChaCha20Poly1305.Tag.FINAL)!
|
||||
let encrypted3 = stream_enc.push(message: message3, tag: .FINAL)!
|
||||
|
||||
/* stream decryption */
|
||||
|
||||
|
@ -257,9 +256,9 @@ let aliceKeyPair = sodium.keyExchange.keyPair()!
|
|||
let bobKeyPair = sodium.keyExchange.keyPair()!
|
||||
|
||||
let sessionKeyPairForAlice = sodium.keyExchange.sessionKeyPair(publicKey: aliceKeyPair.publicKey,
|
||||
secretKey: aliceKeyPair.secretKey, otherPublicKey: bobKeyPair.publicKey, side: .client)!
|
||||
secretKey: aliceKeyPair.secretKey, otherPublicKey: bobKeyPair.publicKey, side: .CLIENT)!
|
||||
let sessionKeyPairForBob = sodium.keyExchange.sessionKeyPair(publicKey: bobKeyPair.publicKey,
|
||||
secretKey: bobKeyPair.secretKey, otherPublicKey: aliceKeyPair.publicKey, side: .server)!
|
||||
secretKey: bobKeyPair.secretKey, otherPublicKey: aliceKeyPair.publicKey, side: .SERVER)!
|
||||
|
||||
let aliceToBobKeyEquality = sodium.utils.equals(sessionKeyPairForAlice.tx, sessionKeyPairForBob.xx) // true
|
||||
let bobToAliceKeyEquality = sodium.utils.equals(sessionKeyPairForAlice.rx, sessionKeyPairForBob.tx) // true
|
||||
|
@ -365,7 +364,7 @@ Constant-time base64 encoding
|
|||
```swift
|
||||
let sodium = Sodium()
|
||||
let b64 = sodium.utils.bin2base64("data".toData()!)!
|
||||
let b64_2 = sodium.utils.bin2base64("data".toData()!, variant: Utils.Base64Variant.URLSAFE_NO_PADDING)!
|
||||
let b64_2 = sodium.utils.bin2base64("data".toData()!, variant: .URLSAFE_NO_PADDING)!
|
||||
```
|
||||
|
||||
Base64 decoding
|
||||
|
@ -374,7 +373,7 @@ Base64 decoding
|
|||
```swift
|
||||
let data1 = sodium.utils.base642bin(b64)
|
||||
let data2 = sodium.utils.base642bin(b64, ignore: " \n")
|
||||
let data3 = sodium.utils.base642bin(b64_2, variant: Utils.Base64Variant.URLSAFE_NO_PADDING, ignore: " \n")
|
||||
let data3 = sodium.utils.base642bin(b64_2, variant: .URLSAFE_NO_PADDING, ignore: " \n")
|
||||
```
|
||||
|
||||
Helpers to build custom constructions
|
||||
|
|
|
@ -2,9 +2,6 @@
|
|||
// Auth.swift
|
||||
// Sodium
|
||||
//
|
||||
// Created by WANG Jie on 03/04/2017.
|
||||
// Copyright © 2017 Frank Denis. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import libsodium
|
||||
|
@ -12,12 +9,12 @@ import libsodium
|
|||
public class Auth {
|
||||
public let KeyBytes = Int(crypto_auth_keybytes())
|
||||
public let Bytes = Int(crypto_auth_bytes())
|
||||
|
||||
|
||||
public typealias SecretKey = Data
|
||||
|
||||
|
||||
/**
|
||||
Generates a key to compute authentication tags.
|
||||
|
||||
|
||||
- Returns: The generated key.
|
||||
*/
|
||||
public func key() -> SecretKey? {
|
||||
|
@ -27,61 +24,55 @@ public class Auth {
|
|||
}
|
||||
return secretKey
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Computes an authentication tag for a message using a key
|
||||
|
||||
|
||||
- Parameter message: The message to authenticate.
|
||||
- Parameter secretKey: The key required to create and verify messages.
|
||||
|
||||
|
||||
- Returns: The computed authentication tag.
|
||||
*/
|
||||
public func tag(message: Data, secretKey: SecretKey) -> Data? {
|
||||
if secretKey.count != KeyBytes {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
var tag = Data(count: Bytes)
|
||||
let result = tag.withUnsafeMutableBytes { tagPtr in
|
||||
return message.withUnsafeBytes { messagePtr in
|
||||
return secretKey.withUnsafeBytes { secretKeyPtr in
|
||||
return crypto_auth(
|
||||
tagPtr,
|
||||
messagePtr,
|
||||
CUnsignedLongLong(message.count),
|
||||
secretKeyPtr)
|
||||
message.withUnsafeBytes { messagePtr in
|
||||
secretKey.withUnsafeBytes { secretKeyPtr in
|
||||
crypto_auth( tagPtr,
|
||||
messagePtr, CUnsignedLongLong(message.count),
|
||||
secretKeyPtr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return tag
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Verifies that an authentication tag is valid for a message and a key
|
||||
|
||||
|
||||
- Parameter message: The message to verify.
|
||||
- Parameter secretKey: The key required to create and verify messages.
|
||||
- Parameter tag: The authentication tag.
|
||||
|
||||
- Returns: `true` if verification is successful.
|
||||
|
||||
- Returns: `true` if the verification is successful.
|
||||
*/
|
||||
public func verify(message: Data, secretKey: SecretKey, tag: Data) -> Bool {
|
||||
if secretKey.count != KeyBytes {
|
||||
return false
|
||||
}
|
||||
|
||||
return tag.withUnsafeBytes { tagPtr in
|
||||
return message.withUnsafeBytes { messagePtr in
|
||||
return secretKey.withUnsafeBytes { secretKeyPtr in
|
||||
return crypto_auth_verify(
|
||||
message.withUnsafeBytes { messagePtr in
|
||||
secretKey.withUnsafeBytes { secretKeyPtr in
|
||||
crypto_auth_verify(
|
||||
tagPtr,
|
||||
messagePtr,
|
||||
CUnsignedLongLong(message.count), secretKeyPtr) == 0
|
||||
messagePtr, CUnsignedLongLong(message.count), secretKeyPtr) == 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
318
Sodium/Box.swift
318
Sodium/Box.swift
|
@ -2,9 +2,6 @@
|
|||
// Box.swift
|
||||
// Sodium
|
||||
//
|
||||
// Created by Frank Denis on 12/28/14.
|
||||
// Copyright (c) 2014 Frank Denis. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import libsodium
|
||||
|
@ -18,75 +15,74 @@ public class Box {
|
|||
public let Primitive = String.init(validatingUTF8:crypto_box_primitive())
|
||||
public let BeforenmBytes = Int(crypto_box_beforenmbytes())
|
||||
public let SealBytes = Int(crypto_box_sealbytes())
|
||||
|
||||
|
||||
public typealias PublicKey = Data
|
||||
public typealias SecretKey = Data
|
||||
public typealias Nonce = Data
|
||||
public typealias MAC = Data
|
||||
public typealias Beforenm = Data
|
||||
|
||||
|
||||
public struct KeyPair {
|
||||
public let publicKey: PublicKey
|
||||
public let secretKey: SecretKey
|
||||
|
||||
|
||||
public init(publicKey: PublicKey, secretKey: SecretKey) {
|
||||
self.publicKey = publicKey
|
||||
self.secretKey = secretKey
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Generates an encryption secret key and a corresponding public key.
|
||||
|
||||
|
||||
- Returns: A key pair containing the secret key and public key.
|
||||
*/
|
||||
public func keyPair() -> KeyPair? {
|
||||
var pk = Data(count: PublicKeyBytes)
|
||||
var sk = Data(count: SecretKeyBytes)
|
||||
let result = pk.withUnsafeMutableBytes { pkPtr in
|
||||
return sk.withUnsafeMutableBytes { skPtr in
|
||||
return crypto_box_keypair(pkPtr, skPtr)
|
||||
sk.withUnsafeMutableBytes { skPtr in
|
||||
crypto_box_keypair(pkPtr, skPtr)
|
||||
}
|
||||
}
|
||||
|
||||
if result != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return KeyPair(publicKey: pk, secretKey: sk)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Generates an encryption secret key and a corresponding public key derived from a seed.
|
||||
|
||||
|
||||
- Parameter seed: The value from which to derive the secret and public key.
|
||||
|
||||
|
||||
- Returns: A key pair containing the secret key and public key.
|
||||
*/
|
||||
public func keyPair(seed: Data) -> KeyPair? {
|
||||
if seed.count != SeedBytes {
|
||||
return nil
|
||||
}
|
||||
|
||||
var pk = Data(count: PublicKeyBytes)
|
||||
var sk = Data(count: SecretKeyBytes)
|
||||
|
||||
let result = pk.withUnsafeMutableBytes { pkPtr in
|
||||
return sk.withUnsafeMutableBytes { skPtr in
|
||||
return seed.withUnsafeBytes { seedPtr in
|
||||
return crypto_box_seed_keypair(pkPtr, skPtr, seedPtr)
|
||||
sk.withUnsafeMutableBytes { skPtr in
|
||||
seed.withUnsafeBytes { seedPtr in
|
||||
crypto_box_seed_keypair(pkPtr, skPtr, seedPtr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return KeyPair(publicKey: pk, secretKey: sk)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Generates a random nonce.
|
||||
|
||||
- Returns: A nonce.
|
||||
*/
|
||||
public func nonce() -> Nonce {
|
||||
var nonce = Data(count: NonceBytes)
|
||||
nonce.withUnsafeMutableBytes { noncePtr in
|
||||
|
@ -94,14 +90,14 @@ public class Box {
|
|||
}
|
||||
return nonce
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Encrypts a message with a recipient's public key and a sender's secret key.
|
||||
|
||||
|
||||
- Parameter message: The message to encrypt.
|
||||
- Parameter recipientPublicKey: The recipient's public key.
|
||||
- Parameter senderSecretKey: The sender's secret key.
|
||||
|
||||
|
||||
- Returns: A `Data` object containing the nonce and authenticated ciphertext.
|
||||
*/
|
||||
public func seal(message: Data, recipientPublicKey: PublicKey, senderSecretKey: SecretKey) -> Data? {
|
||||
|
@ -110,56 +106,53 @@ public class Box {
|
|||
}
|
||||
var nonceAndAuthenticatedCipherText = nonce
|
||||
nonceAndAuthenticatedCipherText.append(authenticatedCipherText)
|
||||
|
||||
return nonceAndAuthenticatedCipherText
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Encrypts a message with a recipient's public key and a sender's secret key using a user-provided nonce.
|
||||
|
||||
|
||||
- Parameter message: The message to encrypt.
|
||||
- Parameter recipientPublicKey: The recipient's public key.
|
||||
- Parameter senderSecretKey: The sender's secret key.
|
||||
- Paramter nonce: The user-specified nonce.
|
||||
|
||||
- Parameter nonce: The user-specified nonce.
|
||||
|
||||
- Returns: The authenticated ciphertext.
|
||||
*/
|
||||
public func seal(message: Data, recipientPublicKey: PublicKey, senderSecretKey: SecretKey, nonce: Nonce) -> Data? {
|
||||
guard recipientPublicKey.count == PublicKeyBytes, senderSecretKey.count == SecretKeyBytes, nonce.count == NonceBytes else { return nil }
|
||||
|
||||
|
||||
var authenticatedCipherText = Data(count: message.count + MacBytes)
|
||||
|
||||
|
||||
let result = authenticatedCipherText.withUnsafeMutableBytes { authenticatedCipherTextPtr in
|
||||
return message.withUnsafeBytes { messagePtr in
|
||||
return nonce.withUnsafeBytes { noncePtr in
|
||||
return recipientPublicKey.withUnsafeBytes { recipientPublicKeyPtr in
|
||||
return senderSecretKey.withUnsafeBytes { senderSecretKeyPtr in
|
||||
return crypto_box_easy(
|
||||
message.withUnsafeBytes { messagePtr in
|
||||
nonce.withUnsafeBytes { noncePtr in
|
||||
recipientPublicKey.withUnsafeBytes { recipientPublicKeyPtr in
|
||||
senderSecretKey.withUnsafeBytes { senderSecretKeyPtr in
|
||||
crypto_box_easy(
|
||||
authenticatedCipherTextPtr,
|
||||
messagePtr,
|
||||
CUnsignedLongLong(message.count),
|
||||
messagePtr, CUnsignedLongLong(message.count),
|
||||
noncePtr,
|
||||
recipientPublicKeyPtr,
|
||||
senderSecretKeyPtr)
|
||||
recipientPublicKeyPtr, senderSecretKeyPtr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return authenticatedCipherText
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Encrypts a message with a recipient's public key and a sender's secret key.
|
||||
|
||||
|
||||
- Parameter message: The message to encrypt.
|
||||
- Parameter recipientPublicKey: The recipient's public key.
|
||||
- Parameter senderSecretKey: The sender's secret key.
|
||||
|
||||
|
||||
- Returns: The authenticated ciphertext and encryption nonce.
|
||||
*/
|
||||
public func seal(message: Data, recipientPublicKey: PublicKey, senderSecretKey: SecretKey) -> (authenticatedCipherText: Data, nonce: Nonce)? {
|
||||
|
@ -168,39 +161,35 @@ public class Box {
|
|||
}
|
||||
var authenticatedCipherText = Data(count: message.count + MacBytes)
|
||||
let nonce = self.nonce()
|
||||
|
||||
|
||||
let result = authenticatedCipherText.withUnsafeMutableBytes { authenticatedCipherTextPtr in
|
||||
return message.withUnsafeBytes { messagePtr in
|
||||
return nonce.withUnsafeBytes { noncePtr in
|
||||
return recipientPublicKey.withUnsafeBytes { recipientPublicKeyPtr in
|
||||
return senderSecretKey.withUnsafeBytes { senderSecretKeyPtr in
|
||||
return crypto_box_easy(
|
||||
message.withUnsafeBytes { messagePtr in
|
||||
nonce.withUnsafeBytes { noncePtr in
|
||||
recipientPublicKey.withUnsafeBytes { recipientPublicKeyPtr in
|
||||
senderSecretKey.withUnsafeBytes { senderSecretKeyPtr in
|
||||
crypto_box_easy(
|
||||
authenticatedCipherTextPtr,
|
||||
messagePtr,
|
||||
CUnsignedLongLong(message.count),
|
||||
messagePtr, CUnsignedLongLong(message.count),
|
||||
noncePtr,
|
||||
recipientPublicKeyPtr,
|
||||
senderSecretKeyPtr)
|
||||
recipientPublicKeyPtr, senderSecretKeyPtr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return (authenticatedCipherText: authenticatedCipherText, nonce: nonce)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Encrypts a message with a recipient's public key and a sender's secret key (detached mode).
|
||||
|
||||
|
||||
- Parameter message: The message to encrypt.
|
||||
- Parameter recipientPublicKey: The recipient's public key.
|
||||
- Parameter senderSecretKey: The sender's secret key.
|
||||
|
||||
|
||||
- Returns: The authenticated ciphertext, encryption nonce, and authentication tag.
|
||||
*/
|
||||
public func seal(message: Data, recipientPublicKey: PublicKey, senderSecretKey: SecretKey) -> (authenticatedCipherText: Data, nonce: Nonce, mac: MAC)? {
|
||||
|
@ -211,40 +200,35 @@ public class Box {
|
|||
var mac = Data(count: MacBytes)
|
||||
let nonce = self.nonce()
|
||||
let result = authenticatedCipherText.withUnsafeMutableBytes { authenticatedCipherTextPtr in
|
||||
return mac.withUnsafeMutableBytes { macPtr in
|
||||
return message.withUnsafeBytes { messagePtr in
|
||||
return nonce.withUnsafeBytes { noncePtr in
|
||||
return recipientPublicKey.withUnsafeBytes { recipientPublicKeyPtr in
|
||||
return senderSecretKey.withUnsafeBytes { senderSecretKeyPtr in
|
||||
return crypto_box_detached(
|
||||
authenticatedCipherTextPtr,
|
||||
macPtr,
|
||||
messagePtr,
|
||||
CUnsignedLongLong(message.count),
|
||||
mac.withUnsafeMutableBytes { macPtr in
|
||||
message.withUnsafeBytes { messagePtr in
|
||||
nonce.withUnsafeBytes { noncePtr in
|
||||
recipientPublicKey.withUnsafeBytes { recipientPublicKeyPtr in
|
||||
senderSecretKey.withUnsafeBytes { senderSecretKeyPtr in
|
||||
crypto_box_detached(
|
||||
authenticatedCipherTextPtr, macPtr,
|
||||
messagePtr, CUnsignedLongLong(message.count),
|
||||
noncePtr,
|
||||
recipientPublicKeyPtr,
|
||||
senderSecretKeyPtr)
|
||||
recipientPublicKeyPtr, senderSecretKeyPtr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return (authenticatedCipherText: authenticatedCipherText, nonce: nonce as Nonce, mac: mac as MAC)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Decrypts a message with a sender's public key and the recipient's secret key.
|
||||
|
||||
|
||||
- Parameter nonceAndAuthenticatedCipherText: A `Data` object containing the nonce and authenticated ciphertext.
|
||||
- Parameter senderPublicKey: The sender's public key.
|
||||
- Parameter recipientSecretKey: The recipient's secret key.
|
||||
|
||||
|
||||
- Returns: The decrypted message.
|
||||
*/
|
||||
public func open(nonceAndAuthenticatedCipherText: Data, senderPublicKey: PublicKey, recipientSecretKey: SecretKey) -> Data? {
|
||||
|
@ -253,63 +237,58 @@ public class Box {
|
|||
}
|
||||
let nonce = nonceAndAuthenticatedCipherText.subdata(in: 0..<NonceBytes) as Nonce
|
||||
let authenticatedCipherText = nonceAndAuthenticatedCipherText.subdata(in: NonceBytes..<nonceAndAuthenticatedCipherText.count)
|
||||
|
||||
return open(authenticatedCipherText: authenticatedCipherText, senderPublicKey: senderPublicKey, recipientSecretKey: recipientSecretKey, nonce: nonce)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Decrypts a message with a sender's public key, recipient's secret key, and encryption nonce.
|
||||
|
||||
|
||||
- Parameter authenticatedCipherText: The authenticated ciphertext.
|
||||
- Parameter senderPublicKey: The sender's public key.
|
||||
- Parameter recipientSecretKey: The recipient's secret key.
|
||||
- Parameter nonce: The encryption nonce.
|
||||
|
||||
|
||||
- Returns: The decrypted message.
|
||||
*/
|
||||
public func open(authenticatedCipherText: Data, senderPublicKey: PublicKey, recipientSecretKey: SecretKey, nonce: Nonce) -> Data? {
|
||||
if nonce.count != NonceBytes || authenticatedCipherText.count < MacBytes {
|
||||
return nil
|
||||
}
|
||||
|
||||
if senderPublicKey.count != PublicKeyBytes || recipientSecretKey.count != SecretKeyBytes {
|
||||
return nil
|
||||
}
|
||||
|
||||
var message = Data(count: authenticatedCipherText.count - MacBytes)
|
||||
let result = message.withUnsafeMutableBytes { messagePtr in
|
||||
return authenticatedCipherText.withUnsafeBytes { authenticatedCipherTextPtr in
|
||||
return nonce.withUnsafeBytes { noncePtr in
|
||||
return senderPublicKey.withUnsafeBytes { senderPublicKeyPtr in
|
||||
return recipientSecretKey.withUnsafeBytes { recipientSecretKeyPtr in
|
||||
return crypto_box_open_easy(
|
||||
messagePtr,
|
||||
authenticatedCipherTextPtr,
|
||||
authenticatedCipherText.withUnsafeBytes { authenticatedCipherTextPtr in
|
||||
nonce.withUnsafeBytes { noncePtr in
|
||||
senderPublicKey.withUnsafeBytes { senderPublicKeyPtr in
|
||||
recipientSecretKey.withUnsafeBytes { recipientSecretKeyPtr in
|
||||
crypto_box_open_easy(
|
||||
messagePtr, authenticatedCipherTextPtr,
|
||||
CUnsignedLongLong(authenticatedCipherText.count),
|
||||
noncePtr,
|
||||
senderPublicKeyPtr,
|
||||
recipientSecretKeyPtr)
|
||||
senderPublicKeyPtr, recipientSecretKeyPtr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return message
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Decrypts a message with a sender's public key, recipient's secret key, encryption nonce, and authentication tag.
|
||||
|
||||
|
||||
- Parameter authenticatedCipherText: The authenticated ciphertext.
|
||||
- Parameter senderPublicKey: The sender's public key.
|
||||
- Parameter recipientSecretKey: The recipient's secret key.
|
||||
- Parameter nonce: The encryption nonce.
|
||||
- Parameter mac: The authentication tag.
|
||||
|
||||
|
||||
- Returns: The decrypted message.
|
||||
*/
|
||||
public func open(authenticatedCipherText: Data, senderPublicKey: PublicKey, recipientSecretKey: SecretKey, nonce: Nonce, mac: MAC) -> Data? {
|
||||
|
@ -320,83 +299,75 @@ public class Box {
|
|||
return nil
|
||||
}
|
||||
var message = Data(count: authenticatedCipherText.count)
|
||||
|
||||
|
||||
let result = message.withUnsafeMutableBytes { messagePtr in
|
||||
return authenticatedCipherText.withUnsafeBytes { authenticatedCipherTextPtr in
|
||||
return mac.withUnsafeBytes { macPtr in
|
||||
return nonce.withUnsafeBytes { noncePtr in
|
||||
return senderPublicKey.withUnsafeBytes { senderPublicKeyPtr in
|
||||
return recipientSecretKey.withUnsafeBytes { recipientSecretKeyPtr in
|
||||
return crypto_box_open_detached(
|
||||
messagePtr,
|
||||
authenticatedCipherTextPtr,
|
||||
macPtr,
|
||||
authenticatedCipherText.withUnsafeBytes { authenticatedCipherTextPtr in
|
||||
mac.withUnsafeBytes { macPtr in
|
||||
nonce.withUnsafeBytes { noncePtr in
|
||||
senderPublicKey.withUnsafeBytes { senderPublicKeyPtr in
|
||||
recipientSecretKey.withUnsafeBytes { recipientSecretKeyPtr in
|
||||
crypto_box_open_detached(
|
||||
messagePtr, authenticatedCipherTextPtr, macPtr,
|
||||
CUnsignedLongLong(authenticatedCipherText.count),
|
||||
noncePtr,
|
||||
senderPublicKeyPtr,
|
||||
recipientSecretKeyPtr)
|
||||
senderPublicKeyPtr, recipientSecretKeyPtr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return message
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Computes a shared secret key given a public key and a secret key.
|
||||
|
||||
|
||||
Applications that send several messages to the same receiver or receive several messages from the same sender can gain speed by calculating the shared key only once, and reusing it in subsequent operations.
|
||||
|
||||
|
||||
- Parameter recipientPublicKey: The recipient's public key.
|
||||
- Parameter senderSecretKey: The sender's secret key.
|
||||
|
||||
- Returns: The computed shared secret key
|
||||
|
||||
- Returns: The computed shared secret key.
|
||||
*/
|
||||
public func beforenm(recipientPublicKey: PublicKey, senderSecretKey: SecretKey) -> Data? {
|
||||
var key = Data(count: BeforenmBytes)
|
||||
let result = key.withUnsafeMutableBytes { keyPtr in
|
||||
return recipientPublicKey.withUnsafeBytes { recipientPublicKeyPtr in
|
||||
return senderSecretKey.withUnsafeBytes { senderSecretKeyPtr in
|
||||
return crypto_box_beforenm(keyPtr, recipientPublicKeyPtr, senderSecretKeyPtr)
|
||||
recipientPublicKey.withUnsafeBytes { recipientPublicKeyPtr in
|
||||
senderSecretKey.withUnsafeBytes { senderSecretKeyPtr in
|
||||
crypto_box_beforenm(keyPtr, recipientPublicKeyPtr, senderSecretKeyPtr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return key
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Encrypts a message with the shared secret key generated from a recipient's public key and a sender's secret key using `beforenm()`.
|
||||
|
||||
|
||||
- Parameter message: The message to encrypt.
|
||||
- Parameter beforenm: The shared secret key.
|
||||
|
||||
|
||||
- Returns: The authenticated ciphertext and encryption nonce.
|
||||
*/
|
||||
public func seal(message: Data, beforenm: Beforenm) -> (authenticatedCipherText: Data, nonce: Nonce)? {
|
||||
if beforenm.count != BeforenmBytes {
|
||||
return nil
|
||||
}
|
||||
|
||||
var authenticatedCipherText = Data(count: message.count + MacBytes)
|
||||
let nonce = self.nonce()
|
||||
|
||||
|
||||
let result = authenticatedCipherText.withUnsafeMutableBytes { authenticatedCipherTextPtr in
|
||||
return message.withUnsafeBytes { messagePtr in
|
||||
return nonce.withUnsafeBytes { noncePtr in
|
||||
return beforenm.withUnsafeBytes { beforenmPtr in
|
||||
return crypto_box_easy_afternm(
|
||||
message.withUnsafeBytes { messagePtr in
|
||||
nonce.withUnsafeBytes { noncePtr in
|
||||
beforenm.withUnsafeBytes { beforenmPtr in
|
||||
crypto_box_easy_afternm(
|
||||
authenticatedCipherTextPtr,
|
||||
messagePtr,
|
||||
CUnsignedLongLong(message.count),
|
||||
|
@ -406,158 +377,143 @@ public class Box {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return (authenticatedCipherText: authenticatedCipherText, nonce: nonce)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Decrypts a message with the shared secret key generated from a recipient's public key and a sender's secret key using `beforenm()`.
|
||||
|
||||
|
||||
- Parameter nonceAndAuthenticatedCipherText: A `Data` object containing the nonce and authenticated ciphertext.
|
||||
- Parameter beforenm: The shared secret key.
|
||||
|
||||
|
||||
- Returns: The decrypted message.
|
||||
*/
|
||||
public func open(nonceAndAuthenticatedCipherText: Data, beforenm: Beforenm) -> Data? {
|
||||
if nonceAndAuthenticatedCipherText.count < NonceBytes + MacBytes {
|
||||
return nil
|
||||
}
|
||||
|
||||
let nonce = nonceAndAuthenticatedCipherText.subdata(in: 0..<NonceBytes) as Nonce
|
||||
let authenticatedCipherText = nonceAndAuthenticatedCipherText.subdata(in: NonceBytes..<nonceAndAuthenticatedCipherText.count)
|
||||
|
||||
return open(authenticatedCipherText: authenticatedCipherText, beforenm: beforenm, nonce: nonce)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Decrypts a message and encryption nonce with the shared secret key generated from a recipient's public key and a sender's secret key using `beforenm()`.
|
||||
|
||||
|
||||
- Parameter authenticatedCipherText: The authenticated ciphertext.
|
||||
- Parameter beforenm: The shared secret key.
|
||||
- Parameter nonce: The encryption nonce.
|
||||
|
||||
|
||||
- Returns: The decrypted message.
|
||||
*/
|
||||
public func open(authenticatedCipherText: Data, beforenm: Beforenm, nonce: Nonce) -> Data? {
|
||||
if nonce.count != NonceBytes || authenticatedCipherText.count < MacBytes {
|
||||
return nil
|
||||
}
|
||||
|
||||
if beforenm.count != BeforenmBytes {
|
||||
return nil
|
||||
}
|
||||
|
||||
var message = Data(count: authenticatedCipherText.count - MacBytes)
|
||||
let result = message.withUnsafeMutableBytes { messagePtr in
|
||||
return authenticatedCipherText.withUnsafeBytes { authenticatedCipherTextPtr in
|
||||
return nonce.withUnsafeBytes { noncePtr in
|
||||
return beforenm.withUnsafeBytes { beforenmPtr in
|
||||
return crypto_box_open_easy_afternm(
|
||||
authenticatedCipherText.withUnsafeBytes { authenticatedCipherTextPtr in
|
||||
nonce.withUnsafeBytes { noncePtr in
|
||||
beforenm.withUnsafeBytes { beforenmPtr in
|
||||
crypto_box_open_easy_afternm(
|
||||
messagePtr,
|
||||
authenticatedCipherTextPtr,
|
||||
CUnsignedLongLong(authenticatedCipherText.count),
|
||||
noncePtr,
|
||||
beforenmPtr)
|
||||
authenticatedCipherTextPtr, CUnsignedLongLong(authenticatedCipherText.count),
|
||||
noncePtr, beforenmPtr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return message
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Encrypts a message with the shared secret key generated from a recipient's public key and a sender's secret key using `beforenm()`.
|
||||
|
||||
|
||||
- Parameter message: The message to encrypt.
|
||||
- Parameter beforenm: The shared secret key.
|
||||
|
||||
|
||||
- Returns: A `Data` object containing the encryption nonce and authenticated ciphertext.
|
||||
*/
|
||||
public func seal(message: Data, beforenm: Beforenm) -> Data? {
|
||||
guard let (authenticatedCipherText, nonce): (Data, Nonce) = seal(message: message, beforenm: beforenm) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
var nonceAndAuthenticatedCipherText = nonce
|
||||
nonceAndAuthenticatedCipherText.append(authenticatedCipherText)
|
||||
|
||||
return nonceAndAuthenticatedCipherText
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Encrypts a message with a recipient's public key.
|
||||
|
||||
|
||||
- Parameter message: The message to encrypt.
|
||||
- Parameter recipientPublicKey: The recipient's public key.
|
||||
|
||||
|
||||
- Returns: The anonymous ciphertext.
|
||||
*/
|
||||
public func seal(message: Data, recipientPublicKey: Box.PublicKey) -> Data? {
|
||||
if recipientPublicKey.count != PublicKeyBytes {
|
||||
return nil
|
||||
}
|
||||
|
||||
var anonymousCipherText = Data(count: SealBytes + message.count)
|
||||
|
||||
let result = anonymousCipherText.withUnsafeMutableBytes { anonymousCipherTextPtr in
|
||||
return message.withUnsafeBytes { messagePtr in
|
||||
return recipientPublicKey.withUnsafeBytes { recipientPublicKeyPtr in
|
||||
return crypto_box_seal(
|
||||
message.withUnsafeBytes { messagePtr in
|
||||
recipientPublicKey.withUnsafeBytes { recipientPublicKeyPtr in
|
||||
crypto_box_seal(
|
||||
anonymousCipherTextPtr,
|
||||
messagePtr,
|
||||
CUnsignedLongLong(message.count),
|
||||
messagePtr, CUnsignedLongLong(message.count),
|
||||
recipientPublicKeyPtr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return anonymousCipherText
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Decrypts a message with the recipient's public key and secret key.
|
||||
|
||||
|
||||
- Parameter anonymousCipherText: A `Data` object containing the anonymous ciphertext.
|
||||
- Parameter senderPublicKey: The recipient's public key.
|
||||
- Parameter recipientSecretKey: The recipient's secret key.
|
||||
|
||||
|
||||
- Returns: The decrypted message.
|
||||
*/
|
||||
public func open(anonymousCipherText: Data, recipientPublicKey: PublicKey, recipientSecretKey: SecretKey) -> Data? {
|
||||
if recipientPublicKey.count != PublicKeyBytes || recipientSecretKey.count != SecretKeyBytes || anonymousCipherText.count < SealBytes {
|
||||
return nil
|
||||
}
|
||||
|
||||
var message = Data(count: anonymousCipherText.count - SealBytes)
|
||||
|
||||
let result = message.withUnsafeMutableBytes { messagePtr in
|
||||
return anonymousCipherText.withUnsafeBytes { anonymousCipherTextPtr in
|
||||
return recipientPublicKey.withUnsafeBytes { recipientPublicKeyPtr in
|
||||
return recipientSecretKey.withUnsafeBytes { recipientSecretKeyPtr in
|
||||
return crypto_box_seal_open(
|
||||
anonymousCipherText.withUnsafeBytes { anonymousCipherTextPtr in
|
||||
recipientPublicKey.withUnsafeBytes { recipientPublicKeyPtr in
|
||||
recipientSecretKey.withUnsafeBytes { recipientSecretKeyPtr in
|
||||
crypto_box_seal_open(
|
||||
messagePtr,
|
||||
anonymousCipherTextPtr,
|
||||
CUnsignedLongLong(anonymousCipherText.count),
|
||||
recipientPublicKeyPtr,
|
||||
recipientSecretKeyPtr)
|
||||
anonymousCipherTextPtr, CUnsignedLongLong(anonymousCipherText.count),
|
||||
recipientPublicKeyPtr, recipientSecretKeyPtr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return message
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,6 @@
|
|||
// GenericHash.swift
|
||||
// Sodium
|
||||
//
|
||||
// Created by Frank Denis on 12/27/14.
|
||||
// Copyright (c) 2014 Frank Denis. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import libsodium
|
||||
|
@ -17,12 +14,12 @@ public class GenericHash {
|
|||
public let KeyBytesMax = Int(crypto_generichash_keybytes_max())
|
||||
public let KeyBytes = Int(crypto_generichash_keybytes())
|
||||
public let Primitive = String.init(validatingUTF8: crypto_generichash_primitive())
|
||||
|
||||
|
||||
public typealias Key = Data
|
||||
|
||||
|
||||
/**
|
||||
Generates a secret key.
|
||||
|
||||
|
||||
- Returns: The generated key.
|
||||
*/
|
||||
public func key() -> Key? {
|
||||
|
@ -32,126 +29,116 @@ public class GenericHash {
|
|||
}
|
||||
return k
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Computes a fixed-length fingerprint for an arbitrary long message. A key can also be specified. A message will always have the same fingerprint for a given key, but different keys used to hash the same message are very likely to produce distinct fingerprints.
|
||||
|
||||
|
||||
- Parameter message: The message from which to compute the fingerprint.
|
||||
- Parameter key: Optional key to use while computing the fingerprint.
|
||||
|
||||
|
||||
- Returns: The computed fingerprint.
|
||||
*/
|
||||
public func hash(message: Data, key: Data? = nil) -> Data? {
|
||||
return hash(message: message, key: key, outputLength: Bytes)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Computes a fixed-length fingerprint for an arbitrary long message. A message will always have the same fingerprint for a given key, but different keys used to hash the same message are very likely to produce distinct fingerprints.
|
||||
|
||||
|
||||
- Parameter message: The message from which to compute the fingerprint.
|
||||
- Parameter key: The key to use while computing the fingerprint.
|
||||
- Parameter outputLength: Desired length of the computed fingerprint.
|
||||
|
||||
|
||||
- Returns: The computed fingerprint.
|
||||
*/
|
||||
public func hash(message: Data, key: Data?, outputLength: Int) -> Data? {
|
||||
var output = Data(count: outputLength)
|
||||
var result: Int32 = -1
|
||||
|
||||
|
||||
if let key = key {
|
||||
result = output.withUnsafeMutableBytes { outputPtr in
|
||||
return message.withUnsafeBytes { messagePtr in
|
||||
return key.withUnsafeBytes { keyPtr in
|
||||
return crypto_generichash(
|
||||
outputPtr,
|
||||
output.count,
|
||||
messagePtr,
|
||||
CUnsignedLongLong(message.count),
|
||||
keyPtr,
|
||||
key.count)
|
||||
message.withUnsafeBytes { messagePtr in
|
||||
key.withUnsafeBytes { keyPtr in
|
||||
crypto_generichash(
|
||||
outputPtr, output.count,
|
||||
messagePtr, CUnsignedLongLong(message.count),
|
||||
keyPtr, key.count)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result = output.withUnsafeMutableBytes { outputPtr in
|
||||
return message.withUnsafeBytes { messagePtr in
|
||||
return crypto_generichash(
|
||||
outputPtr,
|
||||
output.count,
|
||||
messagePtr,
|
||||
CUnsignedLongLong(message.count),
|
||||
nil,
|
||||
0)
|
||||
message.withUnsafeBytes { messagePtr in
|
||||
crypto_generichash(
|
||||
outputPtr, output.count,
|
||||
messagePtr, CUnsignedLongLong(message.count),
|
||||
nil, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Computes a fixed-length fingerprint for an arbitrary long message.
|
||||
|
||||
|
||||
- Parameter message: The message from which to compute the fingerprint.
|
||||
- Parameter outputLength: Desired length of the computed fingerprint.
|
||||
|
||||
|
||||
- Returns: The computed fingerprint.
|
||||
*/
|
||||
public func hash(message: Data, outputLength: Int) -> Data? {
|
||||
return hash(message: message, key: nil, outputLength: outputLength)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Initializes a `Stream` object to compute a fixed-length fingerprint for an incoming stream of data.arbitrary long message. Particular data will always have the same fingerprint for a given key, but different keys used to hash the same data are very likely to produce distinct fingerprints.
|
||||
|
||||
|
||||
- Parameter key: Optional key to use while computing the fingerprint.
|
||||
|
||||
|
||||
- Returns: The initialized `Stream`.
|
||||
*/
|
||||
public func initStream(key: Data? = nil) -> Stream? {
|
||||
return Stream(key: key, outputLength: Bytes)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Initializes a `Stream` object to compute a fixed-length fingerprint for an incoming stream of data.arbitrary long message. Particular data will always have the same fingerprint for a given key, but different keys used to hash the same data are very likely to produce distinct fingerprints.
|
||||
|
||||
|
||||
- Parameter key: Optional key to use while computing the fingerprint.
|
||||
- Parameter outputLength: Desired length of the computed fingerprint.
|
||||
|
||||
|
||||
- Returns: The initialized `Stream`.
|
||||
*/
|
||||
public func initStream(key: Data?, outputLength: Int) -> Stream? {
|
||||
return Stream(key: key, outputLength: outputLength)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Initializes a `Stream` object to compute a fixed-length fingerprint for an incoming stream of data.arbitrary long message.
|
||||
|
||||
|
||||
- Parameter: outputLength: Desired length of the computed fingerprint.
|
||||
|
||||
|
||||
- Returns: The initialized `Stream`.
|
||||
*/
|
||||
public func initStream(outputLength: Int) -> Stream? {
|
||||
return Stream(key: nil, outputLength: outputLength)
|
||||
}
|
||||
|
||||
|
||||
public class Stream {
|
||||
public var outputLength: Int = 0
|
||||
private var state: UnsafeMutablePointer<crypto_generichash_state>?
|
||||
|
||||
|
||||
init?(key: Data?, outputLength: Int) {
|
||||
let rawState = UnsafeMutablePointer<UInt8>.allocate(capacity: crypto_generichash_statebytes())
|
||||
state = UnsafeMutableRawPointer(rawState).bindMemory(to: crypto_generichash_state.self, capacity: 1)
|
||||
guard let state = state else {
|
||||
return nil
|
||||
}
|
||||
|
||||
var result: Int32 = -1
|
||||
|
||||
if let key = key {
|
||||
result = key.withUnsafeBytes { keyPtr in
|
||||
crypto_generichash_init(state, keyPtr, key.count, outputLength)
|
||||
|
@ -159,14 +146,12 @@ public class GenericHash {
|
|||
} else {
|
||||
result = crypto_generichash_init(state, nil, 0, outputLength)
|
||||
}
|
||||
|
||||
if result != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.outputLength = outputLength
|
||||
}
|
||||
|
||||
|
||||
deinit {
|
||||
guard let state = state else {
|
||||
return
|
||||
|
@ -174,23 +159,23 @@ public class GenericHash {
|
|||
let rawState = UnsafeMutableRawPointer(state).bindMemory(to: UInt8.self, capacity: crypto_generichash_statebytes())
|
||||
rawState.deallocate(capacity: 1)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Updates the hash stream with incoming data to contribute to the computed fingerprint.
|
||||
|
||||
|
||||
- Parameter input: The incoming stream data.
|
||||
|
||||
|
||||
- Returns: `true` if the data was consumed successfully.
|
||||
*/
|
||||
public func update(input: Data) -> Bool {
|
||||
return input.withUnsafeBytes { inputPtr in
|
||||
return crypto_generichash_update(state!, inputPtr, CUnsignedLongLong(input.count)) == 0
|
||||
crypto_generichash_update(state!, inputPtr, CUnsignedLongLong(input.count)) == 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Signals that the incoming stream of data is complete and triggers computation of the resulting fingerprint.
|
||||
|
||||
|
||||
- Returns: The computed fingerprint.
|
||||
*/
|
||||
public func final() -> Data? {
|
||||
|
@ -198,11 +183,9 @@ public class GenericHash {
|
|||
let result = output.withUnsafeMutableBytes { outputPtr in
|
||||
crypto_generichash_final(state!, outputPtr, output.count)
|
||||
}
|
||||
|
||||
if result != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,6 @@
|
|||
// KeyDerivation.swift
|
||||
// Sodium
|
||||
//
|
||||
// Created by Patrick Salami (https://www.github.com/psalami) on 7/7/17.
|
||||
// Copyright © 2017 Frank Denis. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import libsodium
|
||||
|
@ -14,13 +11,13 @@ public class KeyDerivation {
|
|||
public let BytesMax = Int(crypto_kdf_bytes_max())
|
||||
public let KeyBytes = Int(crypto_kdf_keybytes())
|
||||
public let ContextBytes = Int(crypto_kdf_contextbytes())
|
||||
|
||||
|
||||
public typealias Key = Data
|
||||
public typealias SubKey = Data
|
||||
|
||||
|
||||
/**
|
||||
Generates a secret key.
|
||||
|
||||
|
||||
- Returns: The generated key.
|
||||
*/
|
||||
public func key() -> Key? {
|
||||
|
@ -30,17 +27,18 @@ public class KeyDerivation {
|
|||
}
|
||||
return k
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Derives a subkey from the specified input key. Each index (from 0 to (2^64) - 1) yields a unique deterministic subkey.
|
||||
The sequence of subkeys is likely unique for a given context.
|
||||
|
||||
|
||||
- Parameter secretKey: the master key from which to derive the subkey (must be KeyBytes bytes)
|
||||
- Parameter index: the index of the subkey to generate (allowed range: 0 to (2^64) - 1)
|
||||
- Parameter length: the desired length of the subkey in bytes (allowed range: BytesMin to BytesMax)
|
||||
- Parameter context: a String that identifies the context; use a different value for different types of keys (should be exactly 8 characters long but must be no longer than 8 characters)
|
||||
|
||||
- Returns: the derived key or nil on error.
|
||||
|
||||
|
||||
- Note: Output keys must have a length between BytesMin and BytesMax bytes (inclusive), otherwise an error is returned. Context must be at most 8 characters long. If the specified context is shorter than 8 characters, it will be padded to 8 characters. The master key is KeyBytes long.
|
||||
*/
|
||||
public func derive(secretKey: Data, index: UInt64, length: Int, context: String) -> Data? {
|
||||
|
@ -54,17 +52,16 @@ public class KeyDerivation {
|
|||
if contextBin.count > ContextBytes {
|
||||
return nil
|
||||
}
|
||||
|
||||
while contextBin.count < ContextBytes {
|
||||
contextBin += [0]
|
||||
}
|
||||
|
||||
|
||||
var output = Data(count: length)
|
||||
|
||||
|
||||
let result = output.withUnsafeMutableBytes { outputPtr in
|
||||
return secretKey.withUnsafeBytes { secretKeyPtr in
|
||||
return contextBin.withUnsafeBytes { contextBinPtr in
|
||||
return crypto_kdf_derive_from_key(outputPtr, length, index, contextBinPtr, secretKeyPtr)
|
||||
secretKey.withUnsafeBytes { secretKeyPtr in
|
||||
contextBin.withUnsafeBytes { contextBinPtr in
|
||||
crypto_kdf_derive_from_key(outputPtr, length, index, contextBinPtr, secretKeyPtr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,145 +2,130 @@
|
|||
// KeyExchange.swift
|
||||
// Sodium
|
||||
//
|
||||
// Created by Andreas Ganske on 17.03.17.
|
||||
// Copyright © 2017 Frank Denis. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import libsodium
|
||||
|
||||
public class KeyExchange {
|
||||
|
||||
public let PublicKeyBytes = Int(crypto_kx_publickeybytes())
|
||||
public let SecretKeyBytes = Int(crypto_kx_secretkeybytes())
|
||||
public let SessionKeyBytes = Int(crypto_kx_sessionkeybytes())
|
||||
public let SeedBytes = Int(crypto_kx_seedbytes())
|
||||
|
||||
|
||||
public typealias PublicKey = Data
|
||||
public typealias SecretKey = Data
|
||||
|
||||
|
||||
public struct KeyPair {
|
||||
public let publicKey: PublicKey
|
||||
public let secretKey: SecretKey
|
||||
|
||||
|
||||
public init(publicKey: PublicKey, secretKey: SecretKey) {
|
||||
self.publicKey = publicKey
|
||||
self.secretKey = secretKey
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public struct SessionKeyPair {
|
||||
public let rx: Data
|
||||
public let tx: Data
|
||||
|
||||
|
||||
public init(rx: Data, tx: Data) {
|
||||
self.rx = rx
|
||||
self.tx = tx
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public enum Side {
|
||||
case client
|
||||
case server
|
||||
case CLIENT
|
||||
case SERVER
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Generates a key exchange secret key and a corresponding public key.
|
||||
|
||||
|
||||
- Returns: A key pair containing the secret key and public key.
|
||||
*/
|
||||
public func keyPair() -> KeyPair? {
|
||||
|
||||
var publicKey = Data(count: PublicKeyBytes)
|
||||
var secretKey = Data(count: SecretKeyBytes)
|
||||
|
||||
var result: Int32 = -1
|
||||
result = publicKey.withUnsafeMutableBytes { publicKeyPtr in
|
||||
return secretKey.withUnsafeMutableBytes { secretKeyPtr in
|
||||
return crypto_kx_keypair(publicKeyPtr, secretKeyPtr)
|
||||
|
||||
let result = publicKey.withUnsafeMutableBytes { publicKeyPtr in
|
||||
secretKey.withUnsafeMutableBytes { secretKeyPtr in
|
||||
crypto_kx_keypair(publicKeyPtr, secretKeyPtr)
|
||||
}
|
||||
}
|
||||
|
||||
guard result == 0 else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return KeyPair(publicKey: publicKey, secretKey: secretKey)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Generates a key exchange secret key and a corresponding public key derived from a seed.
|
||||
|
||||
|
||||
- Parameter seed: The value from which to derive the secret and public key.
|
||||
|
||||
|
||||
- Returns: A key pair containing the secret key and public key.
|
||||
*/
|
||||
public func keyPair(seed: Data) -> KeyPair? {
|
||||
|
||||
if seed.count != SeedBytes {
|
||||
return nil
|
||||
}
|
||||
|
||||
var pk = Data(count: PublicKeyBytes)
|
||||
var sk = Data(count: SecretKeyBytes)
|
||||
|
||||
|
||||
let result = pk.withUnsafeMutableBytes { pkPtr in
|
||||
return sk.withUnsafeMutableBytes { skPtr in
|
||||
return seed.withUnsafeBytes { seedPtr in
|
||||
return crypto_kx_seed_keypair(pkPtr, skPtr, seedPtr)
|
||||
sk.withUnsafeMutableBytes { skPtr in
|
||||
seed.withUnsafeBytes { seedPtr in
|
||||
crypto_kx_seed_keypair(pkPtr, skPtr, seedPtr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return KeyPair(publicKey: pk, secretKey: sk)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Using this function, two parties can securely compute a set of shared keys using their peer's public key and their own secret key.
|
||||
See [libsodium.org/doc/key_exchange](https://download.libsodium.org/doc/key_exchange) for more details.
|
||||
|
||||
|
||||
- Parameter publicKey: The public key used for the key exchange
|
||||
- Parameter secretKey: The secret key to used for the key exchange
|
||||
- Parameter otherPublicKey: The peer's public key for the key exchange
|
||||
- Parameter side: Side (`client` or `host`) on which the key exchange is run
|
||||
|
||||
|
||||
- Returns: A `SessionKeyPair` consisting of a receive (`rx`) key and a transmit (`tx`) key
|
||||
|
||||
|
||||
- Note: `rx` on client side equals `tx` on server side and vice versa.
|
||||
*/
|
||||
public func sessionKeyPair(publicKey: PublicKey, secretKey: SecretKey, otherPublicKey: PublicKey, side: Side) -> SessionKeyPair? {
|
||||
|
||||
if publicKey.count != PublicKeyBytes ||
|
||||
secretKey.count != SecretKeyBytes ||
|
||||
otherPublicKey.count != PublicKeyBytes {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
var rx = Data(count: SessionKeyBytes)
|
||||
var tx = Data(count: SessionKeyBytes)
|
||||
|
||||
let session_keys = (side == .client) ? crypto_kx_client_session_keys : crypto_kx_server_session_keys
|
||||
|
||||
|
||||
let session_keys = (side == .CLIENT) ? crypto_kx_client_session_keys : crypto_kx_server_session_keys
|
||||
|
||||
let result = rx.withUnsafeMutableBytes { rxPtr in
|
||||
return tx.withUnsafeMutableBytes { txPtr in
|
||||
return secretKey.withUnsafeBytes { secretKeyPtr in
|
||||
return publicKey.withUnsafeBytes { publicKeyPtr in
|
||||
return otherPublicKey.withUnsafeBytes { otherPublicKeyPtr in
|
||||
return session_keys(rxPtr, txPtr, publicKeyPtr, secretKeyPtr, otherPublicKeyPtr)
|
||||
tx.withUnsafeMutableBytes { txPtr in
|
||||
secretKey.withUnsafeBytes { secretKeyPtr in
|
||||
publicKey.withUnsafeBytes { publicKeyPtr in
|
||||
otherPublicKey.withUnsafeBytes { otherPublicKeyPtr in
|
||||
session_keys(rxPtr, txPtr, publicKeyPtr, secretKeyPtr, otherPublicKeyPtr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return SessionKeyPair(rx: rx, tx: tx)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,6 @@
|
|||
// PWHash.swift
|
||||
// Sodium
|
||||
//
|
||||
// Created by Frank Denis on 4/29/15.
|
||||
// Copyright (c) 2015 Frank Denis. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import libsodium
|
||||
|
@ -19,77 +16,70 @@ public class PWHash {
|
|||
public let MemLimitInteractive = Int(crypto_pwhash_memlimit_interactive())
|
||||
public let MemLimitModerate = Int(crypto_pwhash_memlimit_moderate())
|
||||
public let MemLimitSensitive = Int(crypto_pwhash_memlimit_sensitive())
|
||||
|
||||
|
||||
public enum Alg {
|
||||
case Default
|
||||
case Argon2I13
|
||||
case Argon2ID13
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Generates an ASCII encoded string, which includes:
|
||||
|
||||
|
||||
- the result of the memory-hard, CPU-intensive Argon2 password hashing function applied to the password `passwd`
|
||||
- the automatically generated salt used for the previous computation
|
||||
- the other parameters required to verify the password, including the algorithm identifier, its version, opslimit and memlimit.
|
||||
|
||||
|
||||
The output string includes only ASCII characters and can be safely stored into SQL databases and other data stores. No extra information has to be stored in order to verify the password.
|
||||
|
||||
|
||||
- Parameter passwd: The password data to hash.
|
||||
- Parameter opsLimit: Represents a maximum amount of computations to perform. Raising this number will make the function require more CPU cycles to compute a key.
|
||||
- Parameter memLimit: The maximum amount of RAM that the function will use, in bytes.
|
||||
|
||||
|
||||
- Returns: The generated string.
|
||||
*/
|
||||
public func str(passwd: Data, opsLimit: Int, memLimit: Int) -> String? {
|
||||
var output = Data(count: StrBytes)
|
||||
let result = output.withUnsafeMutableBytes { outputPtr in
|
||||
return passwd.withUnsafeBytes { passwdPtr in
|
||||
return crypto_pwhash_str(outputPtr,
|
||||
passwdPtr,
|
||||
CUnsignedLongLong(passwd.count),
|
||||
CUnsignedLongLong(opsLimit),
|
||||
size_t(memLimit))
|
||||
passwd.withUnsafeBytes { passwdPtr in
|
||||
crypto_pwhash_str(outputPtr,
|
||||
passwdPtr, CUnsignedLongLong(passwd.count),
|
||||
CUnsignedLongLong(opsLimit), size_t(memLimit))
|
||||
}
|
||||
}
|
||||
|
||||
if result != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return String(data: output, encoding: .utf8)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Verifies that the password str is a valid password verification string (as generated by `str(passwd: Data, opslimit: Int, memLimit: Int)` for `passwd`.
|
||||
|
||||
|
||||
- Parameter hash: The password hash string to verify.
|
||||
- Parameter passwd: The password data to verify.
|
||||
|
||||
|
||||
- Returns: `true` if the verification succeeds.
|
||||
*/
|
||||
public func strVerify(hash: String, passwd: Data) -> Bool {
|
||||
guard let hashData = (hash + "\0").data(using: .utf8, allowLossyConversion: false) else {
|
||||
return false
|
||||
}
|
||||
|
||||
return hashData.withUnsafeBytes { hashPtr in
|
||||
return passwd.withUnsafeBytes { passwdPtr in
|
||||
return crypto_pwhash_str_verify(
|
||||
hashPtr,
|
||||
passwdPtr,
|
||||
CUnsignedLongLong(passwd.count)) == 0
|
||||
passwd.withUnsafeBytes { passwdPtr in
|
||||
crypto_pwhash_str_verify(
|
||||
hashPtr, passwdPtr, CUnsignedLongLong(passwd.count)) == 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Check that a string previously hashed password matches the current algorithm and parameters
|
||||
|
||||
Checks that a string previously hashed password matches the current algorithm and parameters
|
||||
|
||||
- Parameter hash: The password hash string to check.
|
||||
- Parameter opsLimit: Represents a maximum amount of computations to perform. Raising this number will make the function require more CPU cycles to compute a key.
|
||||
- Parameter memLimit: The maximum amount of RAM that the function will use, in bytes.
|
||||
|
||||
|
||||
- Returns: `true` if the password hash should be updated.
|
||||
*/
|
||||
public func strNeedsRehash(hash: String, opsLimit: Int, memLimit: Int) -> Bool {
|
||||
|
@ -97,31 +87,29 @@ public class PWHash {
|
|||
return true
|
||||
}
|
||||
return hashData.withUnsafeBytes { hashPtr in
|
||||
return crypto_pwhash_str_needs_rehash(
|
||||
hashPtr,
|
||||
CUnsignedLongLong(opsLimit), size_t(memLimit)) != 0
|
||||
crypto_pwhash_str_needs_rehash(
|
||||
hashPtr, CUnsignedLongLong(opsLimit), size_t(memLimit)) != 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Derives a key from a password and a salt using the Argon2 password hashing function.
|
||||
|
||||
|
||||
Keep in mind that in order to produce the same key from the same password, the same salt, and the same values for opslimit and memlimit have to be used. Therefore, these parameters have to be stored for each user.
|
||||
|
||||
|
||||
- Parameter outputLength: Desired length of the derived key. Should be at least 16 (128 bits)
|
||||
- Parameter passwd: The password data to hash.
|
||||
- Parameter salt: Unpredicatable salt data. Must have a fixed length of `SaltBytes`.
|
||||
- Parameter opsLimit: Represents a maximum amount of computations to perform. Raising this number will make the function require more CPU cycles to compute a key.
|
||||
- Parameter memLimit: The maximum amount of RAM that the function will use, in bytes.
|
||||
- Parameter alg: The algorithm identifier (.Default, .Argon2I13, .Argon2ID13).
|
||||
|
||||
- Parameter alg: The algorithm identifier (`.Default`, `.Argon2I13`, `.Argon2ID13`).
|
||||
|
||||
- Returns: The derived key data.
|
||||
*/
|
||||
public func hash(outputLength: Int, passwd: Data, salt: Data, opsLimit: Int, memLimit: Int, alg: Alg = .Default) -> Data? {
|
||||
if salt.count != SaltBytes {
|
||||
return nil
|
||||
}
|
||||
|
||||
var output = Data(count: outputLength)
|
||||
var algId: Int32
|
||||
switch alg {
|
||||
|
@ -133,9 +121,9 @@ public class PWHash {
|
|||
algId = crypto_pwhash_alg_argon2id13()
|
||||
}
|
||||
let result = passwd.withUnsafeBytes { passwdPtr in
|
||||
return salt.withUnsafeBytes { saltPtr in
|
||||
return output.withUnsafeMutableBytes { outputPtr in
|
||||
return crypto_pwhash(
|
||||
salt.withUnsafeBytes { saltPtr in
|
||||
output.withUnsafeMutableBytes { outputPtr in
|
||||
crypto_pwhash(
|
||||
outputPtr, CUnsignedLongLong(outputLength),
|
||||
passwdPtr, CUnsignedLongLong(passwd.count),
|
||||
saltPtr, CUnsignedLongLong(opsLimit),
|
||||
|
@ -143,11 +131,9 @@ public class PWHash {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,21 +2,18 @@
|
|||
// RandomBytes.swift
|
||||
// Sodium
|
||||
//
|
||||
// Created by Frank Denis on 12/27/14.
|
||||
// Copyright (c) 2014 Frank Denis. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import libsodium
|
||||
|
||||
public class RandomBytes {
|
||||
public let SeedBytes = Int(randombytes_seedbytes())
|
||||
|
||||
|
||||
/**
|
||||
Returns a `Data object of length `length` containing an unpredictable sequence of bytes.
|
||||
|
||||
|
||||
- Parameter length: The number of bytes to generate.
|
||||
|
||||
|
||||
- Returns: The generated data.
|
||||
*/
|
||||
public func buf(length: Int) -> Data? {
|
||||
|
@ -29,31 +26,31 @@ public class RandomBytes {
|
|||
}
|
||||
return output
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
- Returns: An unpredictable value between 0 and 0xffffffff (included).
|
||||
*/
|
||||
public func random() -> UInt32 {
|
||||
return randombytes_random()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Returns an unpredictable value between 0 and `upper_bound` (excluded). Unlike randombytes_random() % upper_bound, it does its best to guarantee a uniform distribution of the possible output values even when upper_bound is not a power of 2.
|
||||
|
||||
|
||||
- Parameter upperBound: The upper bound (excluded) of the returned value.
|
||||
|
||||
|
||||
- Returns: The unpredictable value.
|
||||
*/
|
||||
public func uniform(upperBound: UInt32) -> UInt32 {
|
||||
return randombytes_uniform(upperBound)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Returns a deterministic stream of unbiased bits derived from a seed.
|
||||
|
||||
|
||||
- Parameter length: The number of bytes to generate.
|
||||
- Parameter seed: The seed.
|
||||
|
||||
|
||||
- Returns: The generated data.
|
||||
*/
|
||||
public func deterministic(length: Int, seed: Data) -> Data? {
|
||||
|
@ -62,7 +59,7 @@ public class RandomBytes {
|
|||
}
|
||||
var output = Data(count: length)
|
||||
output.withUnsafeMutableBytes { outputPtr in
|
||||
return seed.withUnsafeBytes { seedPtr in
|
||||
seed.withUnsafeBytes { seedPtr in
|
||||
randombytes_buf_deterministic(outputPtr, output.count, seedPtr)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,6 @@
|
|||
// SecretBox.swift
|
||||
// Sodium
|
||||
//
|
||||
// Created by Devin Chalmers on 1/4/15.
|
||||
// Copyright (c) 2015 Frank Denis. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import libsodium
|
||||
|
@ -13,14 +10,14 @@ public class SecretBox {
|
|||
public let KeyBytes = Int(crypto_secretbox_keybytes())
|
||||
public let NonceBytes = Int(crypto_secretbox_noncebytes())
|
||||
public let MacBytes = Int(crypto_secretbox_macbytes())
|
||||
|
||||
|
||||
public typealias Key = Data
|
||||
public typealias Nonce = Data
|
||||
public typealias MAC = Data
|
||||
|
||||
|
||||
/**
|
||||
Generates a shared secret key.
|
||||
|
||||
|
||||
- Returns: The generated key.
|
||||
*/
|
||||
public func key() -> Key? {
|
||||
|
@ -30,10 +27,10 @@ public class SecretBox {
|
|||
}
|
||||
return k
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Generates an encryption nonce.
|
||||
|
||||
|
||||
- Returns: The generated nonce.
|
||||
*/
|
||||
public func nonce() -> Nonce {
|
||||
|
@ -43,202 +40,180 @@ public class SecretBox {
|
|||
}
|
||||
return nonce
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Encrypts a message with a shared secret key.
|
||||
|
||||
|
||||
- Parameter message: The message to encrypt.
|
||||
- Parameter secretKey: The shared secret key.
|
||||
|
||||
|
||||
- Returns: A `Data` object containing the nonce and authenticated ciphertext.
|
||||
*/
|
||||
public func seal(message: Data, secretKey: Key) -> Data? {
|
||||
guard let (authenticatedCipherText, nonce): (Data, Nonce) = seal(message: message, secretKey: secretKey) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
var nonceAndAuthenticatedCipherText = nonce
|
||||
nonceAndAuthenticatedCipherText.append(authenticatedCipherText)
|
||||
|
||||
return nonceAndAuthenticatedCipherText
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Encrypts a message with a shared secret key.
|
||||
|
||||
|
||||
- Parameter message: The message to encrypt.
|
||||
- Parameter secretKey: The shared secret key.
|
||||
|
||||
|
||||
- Returns: The authenticated ciphertext and encryption nonce.
|
||||
*/
|
||||
public func seal(message: Data, secretKey: Key) -> (authenticatedCipherText: Data, nonce: Nonce)? {
|
||||
if secretKey.count != KeyBytes {
|
||||
return nil
|
||||
}
|
||||
|
||||
var authenticatedCipherText = Data(count: message.count + MacBytes)
|
||||
let nonce = self.nonce()
|
||||
|
||||
|
||||
let result = authenticatedCipherText.withUnsafeMutableBytes { authenticatedCipherTextPtr in
|
||||
return message.withUnsafeBytes { messagePtr in
|
||||
return nonce.withUnsafeBytes { noncePtr in
|
||||
return secretKey.withUnsafeBytes { secretKeyPtr in
|
||||
return crypto_secretbox_easy(
|
||||
message.withUnsafeBytes { messagePtr in
|
||||
nonce.withUnsafeBytes { noncePtr in
|
||||
secretKey.withUnsafeBytes { secretKeyPtr in
|
||||
crypto_secretbox_easy(
|
||||
authenticatedCipherTextPtr,
|
||||
messagePtr,
|
||||
UInt64(message.count),
|
||||
noncePtr,
|
||||
secretKeyPtr)
|
||||
messagePtr, UInt64(message.count),
|
||||
noncePtr, secretKeyPtr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return (authenticatedCipherText: authenticatedCipherText, nonce: nonce)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Encrypts a message with a shared secret key (detached mode).
|
||||
|
||||
|
||||
- Parameter message: The message to encrypt.
|
||||
- Parameter secretKey: The shared secret key.
|
||||
|
||||
|
||||
- Returns: The encrypted ciphertext, encryption nonce, and authentication tag.
|
||||
*/
|
||||
public func seal(message: Data, secretKey: Key) -> (cipherText: Data, nonce: Nonce, mac: MAC)? {
|
||||
if secretKey.count != KeyBytes {
|
||||
return nil
|
||||
}
|
||||
|
||||
var cipherText = Data(count: message.count)
|
||||
var mac = Data(count: MacBytes)
|
||||
let nonce = self.nonce()
|
||||
|
||||
|
||||
let result = cipherText.withUnsafeMutableBytes { cipherTextPtr in
|
||||
return mac.withUnsafeMutableBytes { macPtr in
|
||||
return message.withUnsafeBytes { messagePtr in
|
||||
return nonce.withUnsafeBytes { noncePtr in
|
||||
return secretKey.withUnsafeBytes { secretKeyPtr in
|
||||
return crypto_secretbox_detached(
|
||||
cipherTextPtr,
|
||||
macPtr,
|
||||
messagePtr,
|
||||
UInt64(message.count),
|
||||
noncePtr,
|
||||
secretKeyPtr)
|
||||
mac.withUnsafeMutableBytes { macPtr in
|
||||
message.withUnsafeBytes { messagePtr in
|
||||
nonce.withUnsafeBytes { noncePtr in
|
||||
secretKey.withUnsafeBytes { secretKeyPtr in
|
||||
crypto_secretbox_detached(
|
||||
cipherTextPtr, macPtr,
|
||||
messagePtr, UInt64(message.count),
|
||||
noncePtr, secretKeyPtr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return (cipherText: cipherText, nonce: nonce, mac: mac)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Decrypts a message with a shared secret key.
|
||||
|
||||
|
||||
- Parameter nonceAndAuthenticatedCipherText: A `Data` object containing the nonce and authenticated ciphertext.
|
||||
- Parameter secretKey: The shared secret key.
|
||||
|
||||
|
||||
- Returns: The decrypted message.
|
||||
*/
|
||||
public func open(nonceAndAuthenticatedCipherText: Data, secretKey: Key) -> Data? {
|
||||
if nonceAndAuthenticatedCipherText.count < MacBytes + NonceBytes {
|
||||
return nil
|
||||
}
|
||||
|
||||
let nonce = nonceAndAuthenticatedCipherText.subdata(in: 0..<NonceBytes) as Nonce
|
||||
let authenticatedCipherText = nonceAndAuthenticatedCipherText.subdata(in: NonceBytes..<nonceAndAuthenticatedCipherText.count)
|
||||
|
||||
return open(authenticatedCipherText: authenticatedCipherText, secretKey: secretKey, nonce: nonce)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Decrypts a message with a shared secret key and encryption nonce.
|
||||
|
||||
|
||||
- Parameter authenticatedCipherText: The authenticated ciphertext.
|
||||
- Parameter secretKey: The shared secret key.
|
||||
- Parameter nonce: The encryption nonce.
|
||||
|
||||
|
||||
- Returns: The decrypted message.
|
||||
*/
|
||||
public func open(authenticatedCipherText: Data, secretKey: Key, nonce: Nonce) -> Data? {
|
||||
if authenticatedCipherText.count < MacBytes {
|
||||
return nil
|
||||
}
|
||||
|
||||
var message = Data(count: authenticatedCipherText.count - MacBytes)
|
||||
|
||||
|
||||
let result = message.withUnsafeMutableBytes { messagePtr in
|
||||
return authenticatedCipherText.withUnsafeBytes { authenticatedCipherTextPtr in
|
||||
return nonce.withUnsafeBytes { noncePtr in
|
||||
return secretKey.withUnsafeBytes { secretKeyPtr in
|
||||
return crypto_secretbox_open_easy(
|
||||
authenticatedCipherText.withUnsafeBytes { authenticatedCipherTextPtr in
|
||||
nonce.withUnsafeBytes { noncePtr in
|
||||
secretKey.withUnsafeBytes { secretKeyPtr in
|
||||
crypto_secretbox_open_easy(
|
||||
messagePtr,
|
||||
authenticatedCipherTextPtr,
|
||||
UInt64(authenticatedCipherText.count),
|
||||
noncePtr,
|
||||
secretKeyPtr)
|
||||
authenticatedCipherTextPtr, UInt64(authenticatedCipherText.count),
|
||||
noncePtr, secretKeyPtr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return message
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Decrypts a message with a shared secret key, encryption nonce, and authentication tag.
|
||||
|
||||
|
||||
- Parameter cipherText: The encrypted ciphertext.
|
||||
- Parameter secretKey: The shared secret key.
|
||||
- Parameter nonce: The encryption nonce.
|
||||
|
||||
|
||||
- Returns: The decrypted message.
|
||||
*/
|
||||
public func open(cipherText: Data, secretKey: Key, nonce: Nonce, mac: MAC) -> Data? {
|
||||
if nonce.count != NonceBytes || mac.count != MacBytes {
|
||||
return nil
|
||||
}
|
||||
|
||||
if secretKey.count != KeyBytes {
|
||||
return nil
|
||||
}
|
||||
|
||||
var message = Data(count: cipherText.count)
|
||||
|
||||
let result = message.withUnsafeMutableBytes { messagePtr in
|
||||
return cipherText.withUnsafeBytes { cipherTextPtr in
|
||||
return mac.withUnsafeBytes { macPtr in
|
||||
return nonce.withUnsafeBytes { noncePtr in
|
||||
return secretKey.withUnsafeBytes { secretKeyPtr in
|
||||
return crypto_secretbox_open_detached(
|
||||
cipherText.withUnsafeBytes { cipherTextPtr in
|
||||
mac.withUnsafeBytes { macPtr in
|
||||
nonce.withUnsafeBytes { noncePtr in
|
||||
secretKey.withUnsafeBytes { secretKeyPtr in
|
||||
crypto_secretbox_open_detached(
|
||||
messagePtr,
|
||||
cipherTextPtr,
|
||||
macPtr,
|
||||
UInt64(cipherText.count),
|
||||
noncePtr,
|
||||
secretKeyPtr)
|
||||
cipherTextPtr, macPtr, UInt64(cipherText.count),
|
||||
noncePtr, secretKeyPtr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return message
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,16 +2,13 @@
|
|||
// SecretStream.swift
|
||||
// Sodium_iOS
|
||||
//
|
||||
// Created by Frank Denis on 9/21/17.
|
||||
// Copyright © 2017 Frank Denis. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import libsodium
|
||||
|
||||
public class SecretStream {
|
||||
public let xchacha20poly1305 = XChaCha20Poly1305()
|
||||
|
||||
|
||||
public class XChaCha20Poly1305 {
|
||||
public static let ABytes = Int(crypto_secretstream_xchacha20poly1305_abytes())
|
||||
public static let HeaderBytes = Int(crypto_secretstream_xchacha20poly1305_headerbytes())
|
||||
|
@ -24,10 +21,10 @@ public class SecretStream {
|
|||
}
|
||||
public typealias Key = Data
|
||||
public typealias Header = Data
|
||||
|
||||
|
||||
/**
|
||||
Generates a secret key.
|
||||
|
||||
|
||||
- Returns: The generated key.
|
||||
*/
|
||||
public func key() -> Key? {
|
||||
|
@ -37,25 +34,41 @@ public class SecretStream {
|
|||
}
|
||||
return secretKey
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Creates a new stream using the secret key `secretKey`
|
||||
|
||||
- Parameter secretKey: The secret key.
|
||||
|
||||
- Returns: A `PushStreamObject`. The stream header can be obtained by
|
||||
calling the `header()` method of that returned object.
|
||||
*/
|
||||
public func initPush(secretKey: Key) -> PushStream? {
|
||||
guard let stream = PushStream(secretKey: secretKey) else {
|
||||
return nil
|
||||
}
|
||||
return stream
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Starts reading a stream, whose header is `header`.
|
||||
|
||||
- Parameter secretKey: The secret key.
|
||||
- Parameter header: The header.
|
||||
|
||||
- Returns: The stream to decrypt messages from.
|
||||
*/
|
||||
public func initPull(secretKey: Key, header: Header) -> PullStream? {
|
||||
guard let stream = PullStream(secretKey: secretKey, header: header) else {
|
||||
return nil
|
||||
}
|
||||
return stream
|
||||
}
|
||||
|
||||
|
||||
public class PushStream {
|
||||
private var state: UnsafeMutablePointer<crypto_secretstream_xchacha20poly1305_state>?
|
||||
private var _header: Header
|
||||
|
||||
|
||||
init?(secretKey: Key) {
|
||||
if secretKey.count != KeyBytes {
|
||||
return nil
|
||||
|
@ -75,20 +88,34 @@ public class SecretStream {
|
|||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
The header of the stream, required to decrypt it.
|
||||
|
||||
- Returns: The stream header.
|
||||
*/
|
||||
public func header() -> Header {
|
||||
return _header
|
||||
}
|
||||
|
||||
public func push(message: Data, tag: Tag = Tag.MESSAGE, ad: Data? = nil) -> Data? {
|
||||
|
||||
/**
|
||||
Encrypts and authenticate a new message. Optionally also authenticate `ad`.
|
||||
|
||||
- Parameter message: The message to encrypt.
|
||||
- Parameter tag: The tag to attach to the message. By default `.MESSAGE`.
|
||||
You may want to use `.FINAL` for the last message of the stream instead.
|
||||
- Parameter ad: Optional additional data to authenticate.
|
||||
|
||||
- Returns: The ciphertext.
|
||||
*/
|
||||
public func push(message: Data, tag: Tag = .MESSAGE, ad: Data? = nil) -> Data? {
|
||||
var _ad = Data(count: 0)
|
||||
if ad != nil {
|
||||
_ad = ad!
|
||||
}
|
||||
let adx = Data(count:0)
|
||||
var cipherText = Data(count: message.count + ABytes)
|
||||
let result = cipherText.withUnsafeMutableBytes { cipherTextPtr in
|
||||
adx.withUnsafeBytes { adPtr in
|
||||
_ad.withUnsafeBytes { adPtr in
|
||||
message.withUnsafeBytes { messagePtr in
|
||||
crypto_secretstream_xchacha20poly1305_push(self.state!, cipherTextPtr, nil, messagePtr, CUnsignedLongLong(message.count), adPtr, CUnsignedLongLong(_ad.count), tag.rawValue)
|
||||
}
|
||||
|
@ -99,11 +126,14 @@ public class SecretStream {
|
|||
}
|
||||
return cipherText
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Performs an explicit key rotation.
|
||||
*/
|
||||
public func rekey() {
|
||||
crypto_secretstream_xchacha20poly1305_rekey(state)
|
||||
}
|
||||
|
||||
|
||||
deinit {
|
||||
guard let state = state else {
|
||||
return
|
||||
|
@ -112,10 +142,10 @@ public class SecretStream {
|
|||
rawState.deallocate(capacity: 1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class PullStream {
|
||||
private var state: UnsafeMutablePointer<crypto_secretstream_xchacha20poly1305_state>?
|
||||
|
||||
|
||||
init?(secretKey: Key, header: Header) {
|
||||
if header.count != HeaderBytes || secretKey.count != KeyBytes {
|
||||
return nil
|
||||
|
@ -134,7 +164,15 @@ public class SecretStream {
|
|||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Decrypts a new message off the stream.
|
||||
|
||||
- Parameter cipherText: The encrypted message.
|
||||
- Parameter ad: Optional additional data to authenticate.
|
||||
|
||||
- Returns: The decrypted message, as well as the tag attached to it.
|
||||
*/
|
||||
public func pull(cipherText: Data, ad: Data? = nil) -> (Data, Tag)? {
|
||||
if cipherText.count < ABytes {
|
||||
return nil
|
||||
|
@ -162,11 +200,14 @@ public class SecretStream {
|
|||
}
|
||||
return (message, tag)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Performs an explicit key rotation.
|
||||
*/
|
||||
public func rekey() {
|
||||
crypto_secretstream_xchacha20poly1305_rekey(state)
|
||||
}
|
||||
|
||||
|
||||
deinit {
|
||||
guard let state = state else {
|
||||
return
|
||||
|
|
|
@ -2,9 +2,6 @@
|
|||
// ShortHash.swift
|
||||
// Sodium
|
||||
//
|
||||
// Created by Frank Denis on 12/28/14.
|
||||
// Copyright (c) 2014 Frank Denis. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import libsodium
|
||||
|
@ -12,12 +9,12 @@ import libsodium
|
|||
public class ShortHash {
|
||||
public let Bytes = Int(crypto_shorthash_bytes())
|
||||
public let KeyBytes = Int(crypto_shorthash_keybytes())
|
||||
|
||||
|
||||
public typealias Key = Data
|
||||
|
||||
|
||||
/**
|
||||
Generates a secret key.
|
||||
|
||||
|
||||
- Returns: The generated key.
|
||||
*/
|
||||
public func key() -> Key? {
|
||||
|
@ -27,34 +24,31 @@ public class ShortHash {
|
|||
}
|
||||
return k
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Computes short but unpredictable (without knowing the secret key) values suitable for picking a list in a hash table for a given key.
|
||||
|
||||
|
||||
- Parameter message: The data to be hashed.
|
||||
- Parameter key: The hash key. Must be of length `KeyBytes`. Can be created using `RandomBytes.buf()`.
|
||||
|
||||
- Returns: The computed fingerprint. Will be of length `Bytes`.
|
||||
|
||||
- Returns: The computed fingerprint.
|
||||
*/
|
||||
public func hash(message: Data, key: Data) -> Data? {
|
||||
if key.count != KeyBytes {
|
||||
return nil
|
||||
}
|
||||
|
||||
var output = Data(count: Bytes)
|
||||
|
||||
|
||||
let result = output.withUnsafeMutableBytes { outputPtr in
|
||||
return message.withUnsafeBytes { messagePtr in
|
||||
return key.withUnsafeBytes { keyPtr in
|
||||
return crypto_shorthash(outputPtr, messagePtr, CUnsignedLongLong(message.count), keyPtr)
|
||||
message.withUnsafeBytes { messagePtr in
|
||||
key.withUnsafeBytes { keyPtr in
|
||||
crypto_shorthash(outputPtr, messagePtr, CUnsignedLongLong(message.count), keyPtr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,6 @@
|
|||
// Sign.swift
|
||||
// Sodium
|
||||
//
|
||||
// Created by Frank Denis on 12/28/14.
|
||||
// Copyright (c) 2014 Frank Denis. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import libsodium
|
||||
|
@ -15,209 +12,197 @@ public class Sign {
|
|||
public let SecretKeyBytes = Int(crypto_sign_secretkeybytes())
|
||||
public let Bytes = Int(crypto_sign_bytes())
|
||||
public let Primitive = String(validatingUTF8: crypto_sign_primitive())
|
||||
|
||||
|
||||
public typealias PublicKey = Data
|
||||
public typealias SecretKey = Data
|
||||
|
||||
|
||||
public struct KeyPair {
|
||||
public let publicKey: PublicKey
|
||||
public let secretKey: SecretKey
|
||||
|
||||
|
||||
public init(publicKey: PublicKey, secretKey: SecretKey) {
|
||||
self.publicKey = publicKey
|
||||
self.secretKey = secretKey
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Generates a signing secret key and a corresponding public key.
|
||||
|
||||
|
||||
- Returns: A key pair containing the secret key and public key.
|
||||
*/
|
||||
public func keyPair() -> KeyPair? {
|
||||
var pk = Data(count: PublicKeyBytes)
|
||||
var sk = Data(count: SecretKeyBytes)
|
||||
|
||||
|
||||
let result = pk.withUnsafeMutableBytes { pkPtr in
|
||||
return sk.withUnsafeMutableBytes { skPtr in
|
||||
return crypto_sign_keypair(pkPtr, skPtr)
|
||||
sk.withUnsafeMutableBytes { skPtr in
|
||||
crypto_sign_keypair(pkPtr, skPtr)
|
||||
}
|
||||
}
|
||||
|
||||
if result != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return KeyPair(publicKey: pk,
|
||||
secretKey: sk)
|
||||
return KeyPair(publicKey: pk, secretKey: sk)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Generates a signing secret key and a corresponding public key derived from a seed.
|
||||
|
||||
|
||||
- Parameter seed: The value from which to derive the secret and public key.
|
||||
|
||||
|
||||
- Returns: A key pair containing the secret key and public key.
|
||||
*/
|
||||
public func keyPair(seed: Data) -> KeyPair? {
|
||||
if seed.count != SeedBytes {
|
||||
return nil
|
||||
}
|
||||
|
||||
var pk = Data(count: PublicKeyBytes)
|
||||
var sk = Data(count: SecretKeyBytes)
|
||||
|
||||
|
||||
let result = pk.withUnsafeMutableBytes { pkPtr in
|
||||
return sk.withUnsafeMutableBytes { skPtr in
|
||||
return seed.withUnsafeBytes { seedPtr in
|
||||
return crypto_sign_seed_keypair(pkPtr, skPtr, seedPtr)
|
||||
sk.withUnsafeMutableBytes { skPtr in
|
||||
seed.withUnsafeBytes { seedPtr in
|
||||
crypto_sign_seed_keypair(pkPtr, skPtr, seedPtr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return KeyPair(publicKey: pk,
|
||||
secretKey: sk)
|
||||
return KeyPair(publicKey: pk, secretKey: sk)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Signs a message with the sender's secret key
|
||||
|
||||
|
||||
- Parameter message: The message to encrypt.
|
||||
- Parameter secretKey: The sender's secret key.
|
||||
|
||||
|
||||
- Returns: The signed message.
|
||||
*/
|
||||
public func sign(message: Data, secretKey: SecretKey) -> Data? {
|
||||
if secretKey.count != SecretKeyBytes {
|
||||
return nil
|
||||
}
|
||||
|
||||
var signedMessage = Data(count: message.count + Bytes)
|
||||
|
||||
let result = signedMessage.withUnsafeMutableBytes { signedMessagePtr in
|
||||
return message.withUnsafeBytes { messagePtr in
|
||||
return secretKey.withUnsafeBytes { secretKeyPtr in
|
||||
return crypto_sign(
|
||||
signedMessagePtr,
|
||||
nil,
|
||||
messagePtr,
|
||||
CUnsignedLongLong(message.count),
|
||||
message.withUnsafeBytes { messagePtr in
|
||||
secretKey.withUnsafeBytes { secretKeyPtr in
|
||||
crypto_sign(
|
||||
signedMessagePtr, nil,
|
||||
messagePtr, CUnsignedLongLong(message.count),
|
||||
secretKeyPtr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return signedMessage
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Computes a detached signature for a message with the sender's secret key.
|
||||
|
||||
|
||||
- Parameter message: The message to encrypt.
|
||||
- Parameter secretKey: The sender's secret key.
|
||||
|
||||
|
||||
- Returns: The computed signature.
|
||||
*/
|
||||
public func signature(message: Data, secretKey: SecretKey) -> Data? {
|
||||
if secretKey.count != SecretKeyBytes {
|
||||
return nil
|
||||
}
|
||||
|
||||
var signature = Data(count: Bytes)
|
||||
|
||||
let result = signature.withUnsafeMutableBytes { signaturePtr in
|
||||
return message.withUnsafeBytes { messagePtr in
|
||||
return secretKey.withUnsafeBytes { secretKeyPtr in
|
||||
return crypto_sign_detached(
|
||||
signaturePtr,
|
||||
nil,
|
||||
messagePtr,
|
||||
CUnsignedLongLong(message.count),
|
||||
message.withUnsafeBytes { messagePtr in
|
||||
secretKey.withUnsafeBytes { secretKeyPtr in
|
||||
crypto_sign_detached(
|
||||
signaturePtr, nil,
|
||||
messagePtr, CUnsignedLongLong(message.count),
|
||||
secretKeyPtr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if result != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
return signature
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Verifies a signed message with the sender's public key.
|
||||
|
||||
|
||||
- Parameter signedMessage: The signed message to verify.
|
||||
- Parameter publicKey: The sender's public key.
|
||||
|
||||
|
||||
- Returns: `true` if verification is successful.
|
||||
*/
|
||||
public func verify(signedMessage: Data, publicKey: PublicKey) -> Bool {
|
||||
let signature = signedMessage.subdata(in: 0..<Bytes) as Data
|
||||
let message = signedMessage.subdata(in: Bytes..<signedMessage.count) as Data
|
||||
|
||||
return verify(message: message, publicKey: publicKey, signature: signature)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Verifies the detached signature of a message with the sender's public key.
|
||||
|
||||
|
||||
- Parameter message: The message to verify.
|
||||
- Parameter publicKey: The sender's public key.
|
||||
- Parameter signature: The detached signature to verify.
|
||||
|
||||
|
||||
- Returns: `true` if verification is successful.
|
||||
*/
|
||||
public func verify(message: Data, publicKey: PublicKey, signature: Data) -> Bool {
|
||||
if publicKey.count != PublicKeyBytes {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
return signature.withUnsafeBytes { signaturePtr in
|
||||
return message.withUnsafeBytes { messagePtr in
|
||||
return publicKey.withUnsafeBytes { publicKeyPtr in
|
||||
return crypto_sign_verify_detached(
|
||||
message.withUnsafeBytes { messagePtr in
|
||||
publicKey.withUnsafeBytes { publicKeyPtr in
|
||||
crypto_sign_verify_detached(
|
||||
signaturePtr,
|
||||
messagePtr,
|
||||
CUnsignedLongLong(message.count), publicKeyPtr) == 0
|
||||
messagePtr, CUnsignedLongLong(message.count), publicKeyPtr) == 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Extracts and returns the message data of a signed message if the signature is verified with the sender's secret key.
|
||||
|
||||
|
||||
- Parameter signedMessage: The signed message to open.
|
||||
- Parameter publicKey: The sender's public key.
|
||||
|
||||
|
||||
- Returns: The message data if verification is successful.
|
||||
*/
|
||||
public func open(signedMessage: Data, publicKey: PublicKey) -> Data? {
|
||||
if publicKey.count != PublicKeyBytes || signedMessage.count < Bytes {
|
||||
return nil
|
||||
}
|
||||
|
||||
var message = Data(count: signedMessage.count - Bytes)
|
||||
var mlen: CUnsignedLongLong = 0
|
||||
|
||||
let result = message.withUnsafeMutableBytes { messagePtr in
|
||||
return signedMessage.withUnsafeBytes { signedMessagePtr in
|
||||
return publicKey.withUnsafeBytes { publicKeyPtr in
|
||||
return crypto_sign_open(messagePtr, &mlen, signedMessagePtr, CUnsignedLongLong(signedMessage.count), publicKeyPtr)
|
||||
signedMessage.withUnsafeBytes { signedMessagePtr in
|
||||
publicKey.withUnsafeBytes { publicKeyPtr in
|
||||
crypto_sign_open(
|
||||
messagePtr, &mlen,
|
||||
signedMessagePtr, CUnsignedLongLong(signedMessage.count),
|
||||
publicKeyPtr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return message
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,6 @@
|
|||
// Sodium.swift
|
||||
// Sodium
|
||||
//
|
||||
// Created by Frank Denis on 12/27/14.
|
||||
// Copyright (c) 2014 Frank Denis. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import libsodium
|
||||
|
@ -23,13 +20,13 @@ public class Sodium {
|
|||
public let stream = Stream()
|
||||
public let keyDerivation = KeyDerivation()
|
||||
public let secretStream = SecretStream()
|
||||
|
||||
|
||||
private static let once: Void = {
|
||||
if sodium_init() == -1 {
|
||||
fatalError("Failed to initialize libSodium")
|
||||
if sodium_init() < 0 {
|
||||
fatalError("Failed to initialize libsodium")
|
||||
}
|
||||
}()
|
||||
|
||||
|
||||
public init() {
|
||||
_ = Sodium.once
|
||||
}
|
||||
|
|
|
@ -2,9 +2,6 @@
|
|||
// Stream.swift
|
||||
// Sodium
|
||||
//
|
||||
// Created by Frank Denis on 5/30/17.
|
||||
// Copyright © 2017 Frank Denis. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import libsodium
|
||||
|
@ -13,13 +10,13 @@ public class Stream {
|
|||
public let KeyBytes = Int(crypto_secretbox_keybytes())
|
||||
public let NonceBytes = Int(crypto_secretbox_noncebytes())
|
||||
public let Primitive = String.init(validatingUTF8: crypto_stream_primitive())
|
||||
|
||||
|
||||
public typealias Key = Data
|
||||
public typealias Nonce = Data
|
||||
|
||||
|
||||
/**
|
||||
Generates a secret key.
|
||||
|
||||
|
||||
- Returns: The generated key.
|
||||
*/
|
||||
public func key() -> Key? {
|
||||
|
@ -29,10 +26,10 @@ public class Stream {
|
|||
}
|
||||
return k
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Generates a random nonce.
|
||||
|
||||
|
||||
- Returns: The generated nonce.
|
||||
*/
|
||||
public func nonce() -> Nonce {
|
||||
|
@ -42,14 +39,18 @@ public class Stream {
|
|||
}
|
||||
return nonce
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
XOR the input with a key stream derived from a secret key and a nonce.
|
||||
Applying the same operation twice outputs the original input.
|
||||
No authentication tag is added to the output. The data can be tampered with; an adversary can flip arbitrary bits.
|
||||
In order to encrypt data using a secret key, the SecretBox class is likely to be what you are looking for.
|
||||
In order to generate a deterministic stream out of a seed, the RandomBytes.deterministic_rand() function is likely to be what you need.
|
||||
|
||||
|
||||
- Parameter input: Input data
|
||||
- Parameter nonce: Nonce
|
||||
- Parameter secretKey: The secret key
|
||||
|
||||
- Returns: input XOR keystream(secretKey, nonce)
|
||||
*/
|
||||
public func xor(input: Data, nonce: Nonce, secretKey: Key) -> Data? {
|
||||
|
@ -59,28 +60,30 @@ public class Stream {
|
|||
var output = Data(count: input.count)
|
||||
let result = output.withUnsafeMutableBytes { outputPtr in
|
||||
input.withUnsafeBytes { inputPtr in
|
||||
return nonce.withUnsafeBytes { noncePtr in
|
||||
return secretKey.withUnsafeBytes { secretKeyPtr in
|
||||
return crypto_stream_xor(outputPtr, inputPtr, UInt64(input.count), noncePtr, secretKeyPtr)
|
||||
nonce.withUnsafeBytes { noncePtr in
|
||||
secretKey.withUnsafeBytes { secretKeyPtr in
|
||||
crypto_stream_xor(outputPtr, inputPtr, UInt64(input.count), noncePtr, secretKeyPtr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
XOR the input with a key stream derived from a secret key and a random nonce.
|
||||
Applying the same operation twice outputs the original input.
|
||||
No authentication tag is added to the output. The data can be tampered with; an adversary can flip arbitrary bits.
|
||||
In order to encrypt data using a secret key, the SecretBox class is likely to be what you are looking for.
|
||||
In order to generate a deterministic stream out of a seed, the RandomBytes.deterministic_rand() function is likely to be what you need.
|
||||
|
||||
|
||||
- Parameter input: Input data
|
||||
- Parameter nonce: Nonce
|
||||
- Parameter secretKey: The secret key
|
||||
|
||||
- Returns: (input XOR keystream(secretKey, nonce), nonce)
|
||||
*/
|
||||
public func xor(input: Data, secretKey: Key) -> (output:Data, nonce: Nonce)? {
|
||||
|
|
|
@ -2,18 +2,14 @@
|
|||
// Utils.swift
|
||||
// Sodium
|
||||
//
|
||||
// Created by Frank Denis on 12/27/14.
|
||||
// Copyright (c) 2014 Frank Denis. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import libsodium
|
||||
|
||||
public class Utils {
|
||||
|
||||
/**
|
||||
Tries to effectively zero bytes in `data`, even if optimizations are being applied to the code.
|
||||
|
||||
|
||||
- Parameter data: The `Data` object to zero.
|
||||
*/
|
||||
public func zero(_ data: inout Data) {
|
||||
|
@ -21,174 +17,166 @@ public class Utils {
|
|||
data.withUnsafeMutableBytes { (dataPtr: UnsafeMutablePointer<UInt8>) in
|
||||
let rawPtr = UnsafeMutableRawPointer(dataPtr)
|
||||
sodium_memzero(rawPtr, count)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Checks that two `Data` objects have the same content, without leaking information
|
||||
about the actual content of these objects.
|
||||
|
||||
- Parameter b1: first object
|
||||
- Parameter b2: second object
|
||||
|
||||
- Returns: `true` if the bytes in `b1` match the bytes in `b2`. Otherwise, it returns false.
|
||||
*/
|
||||
public func equals(_ b1: Data, _ b2: Data) -> Bool {
|
||||
if b1.count != b2.count {
|
||||
return false
|
||||
}
|
||||
|
||||
return b1.withUnsafeBytes { b1Ptr in
|
||||
return b2.withUnsafeBytes { b2Ptr in
|
||||
return Int(sodium_memcmp(
|
||||
UnsafeRawPointer(b1Ptr),
|
||||
UnsafeRawPointer(b2Ptr),
|
||||
b1.count)) == 0
|
||||
b2.withUnsafeBytes { b2Ptr in
|
||||
Int(sodium_memcmp(
|
||||
UnsafeRawPointer(b1Ptr), UnsafeRawPointer(b2Ptr), b1.count)) == 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
- Returns: 0 if the bytes in `b1` match the bytes in `b2`. Otherwise, it returns -1.
|
||||
Compares two `Data` objects without leaking information about the content of these objects.
|
||||
|
||||
- Returns: `0` if the bytes in `b1` match the bytes in `b2`.
|
||||
`-1` if `b2` is less than `b1` (considered as little-endian values) and
|
||||
`1` if `b1` is less than `b2` (considered as little-endian values)
|
||||
*/
|
||||
public func compare(_ b1: Data, _ b2: Data) -> Int? {
|
||||
if b1.count != b2.count {
|
||||
return nil
|
||||
}
|
||||
|
||||
return b1.withUnsafeBytes { b1Ptr in
|
||||
return b2.withUnsafeBytes { b2Ptr in
|
||||
return Int(sodium_compare(
|
||||
b1Ptr,
|
||||
b2Ptr,
|
||||
b1.count))
|
||||
b2.withUnsafeBytes { b2Ptr in
|
||||
Int(sodium_compare(
|
||||
b1Ptr, b2Ptr, b1.count))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Converts bytes stored in `bin` into a hexadecimal string.
|
||||
|
||||
|
||||
- Parameter bin: The data to encode as hexdecimal.
|
||||
|
||||
|
||||
- Returns: The encoded hexdecimal string.
|
||||
*/
|
||||
public func bin2hex(_ bin: Data) -> String? {
|
||||
var hexData = Data(count: bin.count * 2 + 1)
|
||||
|
||||
return hexData.withUnsafeMutableBytes { (hexPtr: UnsafeMutablePointer<Int8>) -> String? in
|
||||
return bin.withUnsafeBytes { (binPtr: UnsafePointer<UInt8>) -> String? in
|
||||
bin.withUnsafeBytes { (binPtr: UnsafePointer<UInt8>) -> String? in
|
||||
if sodium_bin2hex(hexPtr, hexData.count, binPtr, bin.count) == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return String.init(validatingUTF8: hexPtr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Decode as a hexdecimal string, ignoring characters included for readability.
|
||||
|
||||
Decodes a hexdecimal string, ignoring characters included for readability.
|
||||
|
||||
- Parameter hex: The hexdecimal string to decode.
|
||||
- Parameter ignore: Optional string containing readability characters to ignore during decoding.
|
||||
|
||||
|
||||
- Returns: The decoded data.
|
||||
*/
|
||||
public func hex2bin(_ hex: String, ignore: String? = nil) -> Data? {
|
||||
guard let hexData = hex.data(using: .utf8, allowLossyConversion: false) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let hexDataLen = hexData.count
|
||||
let binDataCapacity = hexDataLen / 2
|
||||
var binData = Data(count: binDataCapacity)
|
||||
var binDataLen: size_t = 0
|
||||
let ignore_cstr = ignore != nil ? (ignore! as NSString).utf8String : nil
|
||||
|
||||
|
||||
let result = binData.withUnsafeMutableBytes { binPtr in
|
||||
return hexData.withUnsafeBytes { hexPtr in
|
||||
return sodium_hex2bin(binPtr,
|
||||
binDataCapacity,
|
||||
hexPtr,
|
||||
hexDataLen,
|
||||
ignore_cstr,
|
||||
&binDataLen,
|
||||
nil)
|
||||
hexData.withUnsafeBytes { hexPtr in
|
||||
sodium_hex2bin(binPtr, binDataCapacity,
|
||||
hexPtr, hexDataLen,
|
||||
ignore_cstr, &binDataLen, nil)
|
||||
}
|
||||
}
|
||||
|
||||
if result != 0 {
|
||||
if result != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
binData.count = Int(binDataLen)
|
||||
|
||||
return binData
|
||||
}
|
||||
|
||||
|
||||
public enum Base64Variant: CInt {
|
||||
case ORIGINAL = 1
|
||||
case ORIGINAL_NO_PADDING = 3
|
||||
case URLSAFE = 5
|
||||
case URLSAFE_NO_PADDING = 7
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Converts bytes stored in `bin` into a Base64 representation.
|
||||
|
||||
|
||||
- Parameter bin: The data to encode as Base64.
|
||||
- Parameter variant: the Base64 variant to use. By default: URLSAFE.
|
||||
|
||||
|
||||
- Returns: The encoded base64 string.
|
||||
*/
|
||||
public func bin2base64(_ bin: Data, variant: Base64Variant = Base64Variant.URLSAFE) -> String? {
|
||||
public func bin2base64(_ bin: Data, variant: Base64Variant = .URLSAFE) -> String? {
|
||||
var b64Data = Data(count: sodium_base64_encoded_len(bin.count, variant.rawValue))
|
||||
|
||||
return b64Data.withUnsafeMutableBytes { (b64Ptr: UnsafeMutablePointer<Int8>) -> String? in
|
||||
return bin.withUnsafeBytes { (binPtr: UnsafePointer<UInt8>) -> String? in
|
||||
bin.withUnsafeBytes { (binPtr: UnsafePointer<UInt8>) -> String? in
|
||||
if sodium_bin2base64(b64Ptr, b64Data.count, binPtr, bin.count, variant.rawValue) == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return String.init(validatingUTF8: b64Ptr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Decode as a base64 string, ignoring characters included for readability.
|
||||
|
||||
- Parameter b64: The base64 string to decode.
|
||||
Decodes a Base64 string, ignoring characters included for readability.
|
||||
|
||||
- Parameter b64: The Base64 string to decode.
|
||||
- Parameter ignore: Optional string containing readability characters to ignore during decoding.
|
||||
|
||||
|
||||
- Returns: The decoded data.
|
||||
*/
|
||||
public func base642bin(_ b64: String, variant: Base64Variant = Base64Variant.URLSAFE, ignore: String? = nil) -> Data? {
|
||||
public func base642bin(_ b64: String, variant: Base64Variant = .URLSAFE, ignore: String? = nil) -> Data? {
|
||||
guard let b64Data = b64.data(using: .utf8, allowLossyConversion: false) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let b64DataLen = b64Data.count
|
||||
let binDataCapacity = b64DataLen * 3 / 4
|
||||
var binData = Data(count: binDataCapacity)
|
||||
var binDataLen: size_t = 0
|
||||
let ignore_cstr = ignore != nil ? (ignore! as NSString).utf8String : nil
|
||||
|
||||
|
||||
let result = binData.withUnsafeMutableBytes { binPtr in
|
||||
b64Data.withUnsafeBytes { b64Ptr in
|
||||
sodium_base642bin(binPtr,
|
||||
binDataCapacity,
|
||||
b64Ptr,
|
||||
b64DataLen,
|
||||
ignore_cstr,
|
||||
&binDataLen,
|
||||
nil, variant.rawValue)
|
||||
sodium_base642bin(binPtr, binDataCapacity,
|
||||
b64Ptr, b64DataLen,
|
||||
ignore_cstr, &binDataLen, nil, variant.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
if result != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
binData.count = Int(binDataLen)
|
||||
|
||||
return binData
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Add padding to `data` so that its length becomes a multiple of `blockSize`
|
||||
|
||||
Adds padding to `data` so that its length becomes a multiple of `blockSize`
|
||||
|
||||
- Parameter data: input/output buffer, will be modified in-place
|
||||
- Parameter blocksize: the block size
|
||||
*/
|
||||
|
@ -204,13 +192,13 @@ public class Utils {
|
|||
return nil
|
||||
}
|
||||
data.count = Int(paddedLen)
|
||||
|
||||
|
||||
return ()
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Remove padding from `data` to restore its original size
|
||||
|
||||
Removes padding from `data` to restore its original size
|
||||
|
||||
- Parameter data: input/output buffer, will be modified in-place
|
||||
- Parameter blocksize: the block size
|
||||
*/
|
||||
|
@ -223,7 +211,7 @@ public class Utils {
|
|||
return nil
|
||||
}
|
||||
data.count = Int(unpaddedLen)
|
||||
|
||||
|
||||
return ()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -223,8 +223,7 @@ class ReadmeTests : XCTestCase {
|
|||
let header = stream_enc.header()
|
||||
let encrypted1 = stream_enc.push(message: message1)!
|
||||
let encrypted2 = stream_enc.push(message: message2)!
|
||||
let encrypted3 = stream_enc.push(message: message3,
|
||||
tag: SecretStream.XChaCha20Poly1305.Tag.FINAL)!
|
||||
let encrypted3 = stream_enc.push(message: message3, tag: .FINAL)!
|
||||
|
||||
/* stream decryption */
|
||||
|
||||
|
@ -236,19 +235,19 @@ class ReadmeTests : XCTestCase {
|
|||
XCTAssertEqual(message1, message1_dec)
|
||||
XCTAssertEqual(message2, message2_dec)
|
||||
XCTAssertEqual(message3, message3_dec)
|
||||
XCTAssertEqual(tag1, SecretStream.XChaCha20Poly1305.Tag.MESSAGE)
|
||||
XCTAssertEqual(tag2, SecretStream.XChaCha20Poly1305.Tag.MESSAGE)
|
||||
XCTAssertEqual(tag3, SecretStream.XChaCha20Poly1305.Tag.FINAL)
|
||||
XCTAssertEqual(tag1, .MESSAGE)
|
||||
XCTAssertEqual(tag2, .MESSAGE)
|
||||
XCTAssertEqual(tag3, .FINAL)
|
||||
}
|
||||
|
||||
func testBase64() {
|
||||
let sodium = Sodium()
|
||||
let b64 = sodium.utils.bin2base64("data".toData()!)!
|
||||
let b64_2 = sodium.utils.bin2base64("data".toData()!, variant: Utils.Base64Variant.URLSAFE_NO_PADDING)!
|
||||
let b64_2 = sodium.utils.bin2base64("data".toData()!, variant: .URLSAFE_NO_PADDING)!
|
||||
|
||||
let data1 = sodium.utils.base642bin(b64)
|
||||
let data2 = sodium.utils.base642bin(b64, ignore: " \n")
|
||||
let data3 = sodium.utils.base642bin(b64_2, variant: Utils.Base64Variant.URLSAFE_NO_PADDING, ignore: " \n")
|
||||
let data3 = sodium.utils.base642bin(b64_2, variant: .URLSAFE_NO_PADDING, ignore: " \n")
|
||||
|
||||
XCTAssertEqual(data1, "data".toData())
|
||||
XCTAssertEqual(data2, "data".toData())
|
||||
|
|
|
@ -241,8 +241,8 @@ class SodiumTests: XCTestCase {
|
|||
let aliceKeyPair = sodium.keyExchange.keyPair()!
|
||||
let bobKeyPair = sodium.keyExchange.keyPair()!
|
||||
|
||||
let sessionKeyPairForAlice = sodium.keyExchange.sessionKeyPair(publicKey: aliceKeyPair.publicKey, secretKey: aliceKeyPair.secretKey, otherPublicKey: bobKeyPair.publicKey, side: .client)!
|
||||
let sessionKeyPairForBob = sodium.keyExchange.sessionKeyPair(publicKey: bobKeyPair.publicKey, secretKey: bobKeyPair.secretKey, otherPublicKey: aliceKeyPair.publicKey, side: .server)!
|
||||
let sessionKeyPairForAlice = sodium.keyExchange.sessionKeyPair(publicKey: aliceKeyPair.publicKey, secretKey: aliceKeyPair.secretKey, otherPublicKey: bobKeyPair.publicKey, side: .CLIENT)!
|
||||
let sessionKeyPairForBob = sodium.keyExchange.sessionKeyPair(publicKey: bobKeyPair.publicKey, secretKey: bobKeyPair.secretKey, otherPublicKey: aliceKeyPair.publicKey, side: .SERVER)!
|
||||
|
||||
XCTAssertEqual(sessionKeyPairForAlice.rx, sessionKeyPairForBob.tx)
|
||||
XCTAssertEqual(sessionKeyPairForAlice.tx, sessionKeyPairForBob.rx)
|
||||
|
@ -328,15 +328,15 @@ class SodiumTests: XCTestCase {
|
|||
let header = stream.header()
|
||||
let encrypted1 = stream.push(message: "message 1".toData()!)!
|
||||
let encrypted2 = stream.push(message: "message 2".toData()!)!
|
||||
let encrypted3 = stream.push(message: "message 3".toData()!, tag: SecretStream.XChaCha20Poly1305.Tag.FINAL)!
|
||||
let encrypted3 = stream.push(message: "message 3".toData()!, tag: .FINAL)!
|
||||
|
||||
let stream2 = sodium.secretStream.xchacha20poly1305.initPull(secretKey: secretKey, header: header)!
|
||||
let (message1, tag1) = stream2.pull(cipherText: encrypted1)!
|
||||
let (message2, tag2) = stream2.pull(cipherText: encrypted2)!
|
||||
let (message3, tag3) = stream2.pull(cipherText: encrypted3)!
|
||||
XCTAssertEqual(tag1, SecretStream.XChaCha20Poly1305.Tag.MESSAGE)
|
||||
XCTAssertEqual(tag2, SecretStream.XChaCha20Poly1305.Tag.MESSAGE)
|
||||
XCTAssertEqual(tag3, SecretStream.XChaCha20Poly1305.Tag.FINAL)
|
||||
XCTAssertEqual(tag1, .MESSAGE)
|
||||
XCTAssertEqual(tag2, .MESSAGE)
|
||||
XCTAssertEqual(tag3, .FINAL)
|
||||
XCTAssertEqual(message1, "message 1".toData()!)
|
||||
XCTAssertEqual(message2, "message 2".toData()!)
|
||||
XCTAssertEqual(message3, "message 3".toData()!)
|
||||
|
@ -350,8 +350,8 @@ class SodiumTests: XCTestCase {
|
|||
XCTAssertEqual(b64, "dGVzdA==")
|
||||
XCTAssertEqual(bin2, bin)
|
||||
|
||||
let b64_nopad = sodium.utils.bin2base64(bin, variant: Utils.Base64Variant.URLSAFE_NO_PADDING)!
|
||||
let bin2_nopad = sodium.utils.base642bin(b64_nopad, variant: Utils.Base64Variant.URLSAFE_NO_PADDING)!
|
||||
let b64_nopad = sodium.utils.bin2base64(bin, variant: .URLSAFE_NO_PADDING)!
|
||||
let bin2_nopad = sodium.utils.base642bin(b64_nopad, variant: .URLSAFE_NO_PADDING)!
|
||||
XCTAssertEqual(b64_nopad, "dGVzdA")
|
||||
XCTAssertEqual(bin2_nopad, bin)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue